提交 f5a609cd authored 作者: 王鹏飞's avatar 王鹏飞

feat(data): 新增内置数据集功能

- 添加创建、删除和列出内置数据集的 API 接口 - 实现数据集创建和删除的 React Hook - 开发数据集列表和表单组件 - 集成 AI 聊天功能 - 优化文件上传组件
上级 1dd30bce
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
"zustand": "^5.0.3" "zustand": "^5.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/blueimp-md5": "^2.18.2",
"@types/node": "^22.13.9", "@types/node": "^22.13.9",
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
...@@ -1421,6 +1422,13 @@ ...@@ -1421,6 +1422,13 @@
"react": "^18 || ^19" "react": "^18 || ^19"
} }
}, },
"node_modules/@types/blueimp-md5": {
"version": "2.18.2",
"resolved": "https://registry.npmjs.org/@types/blueimp-md5/-/blueimp-md5-2.18.2.tgz",
"integrity": "sha512-dJ9yRry9Olt5GAWlgCtE5dK9d/Dfhn/V7hna86eEO2Pn76+E8Y0S0n61iEUEGhWXXgtKtHxtZLVNwL8X+vLHzg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/cookie": { "node_modules/@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"zustand": "^5.0.3" "zustand": "^5.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/blueimp-md5": "^2.18.2",
"@types/node": "^22.13.9", "@types/node": "^22.13.9",
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
......
...@@ -6,11 +6,11 @@ import { useQuery } from '@tanstack/react-query' ...@@ -6,11 +6,11 @@ import { useQuery } from '@tanstack/react-query'
export interface QueryParams { export interface QueryParams {
[key: string]: any [key: string]: any
page: number page: number
pageSize: number 'per-page': number
} }
export interface AppListProps extends TableProps { export interface AppListProps extends TableProps {
fetchApi?: (params: QueryParams) => Promise<{ list: any[]; total: number }> fetchApi?: (params?: QueryParams) => Promise<{ list: any[]; total: number }>
filters?: any[] filters?: any[]
filterAside?: React.ReactNode filterAside?: React.ReactNode
} }
...@@ -24,13 +24,12 @@ const AppList = forwardRef<AppListRef, AppListProps>((props, ref) => { ...@@ -24,13 +24,12 @@ const AppList = forwardRef<AppListRef, AppListProps>((props, ref) => {
const { fetchApi, filters = [], filterAside, dataSource = [], ...rest } = props const { fetchApi, filters = [], filterAside, dataSource = [], ...rest } = props
const [form] = Form.useForm() const [form] = Form.useForm()
const [queryParams, setQueryParams] = useState<QueryParams>({ page: 1, pageSize: 10 }) const [queryParams, setQueryParams] = useState<QueryParams>({ page: 1, 'per-page': 10 })
const { data, refetch, isLoading } = useQuery({ const { data, refetch, isLoading } = useQuery({
queryKey: ['appList', queryParams], queryKey: ['appList', queryParams],
queryFn: async () => { queryFn: async () => {
const result = fetchApi ? await fetchApi(queryParams) : { list: dataSource || [], total: dataSource?.length || 0 } return fetchApi ? await fetchApi(queryParams) : { list: dataSource || [], total: dataSource?.length || 0 }
return { ...result, list: result.list as any[] }
}, },
}) })
...@@ -48,12 +47,12 @@ const AppList = forwardRef<AppListRef, AppListProps>((props, ref) => { ...@@ -48,12 +47,12 @@ const AppList = forwardRef<AppListRef, AppListProps>((props, ref) => {
const handleReset = () => { const handleReset = () => {
form.resetFields() form.resetFields()
setQueryParams({ page: 1, pageSize: 10 }) setQueryParams({ page: 1, 'per-page': 10 })
} }
// 处理分页 // 处理分页
const handlePageChange = (page: number, pageSize: number) => { const handlePageChange = (page: number, pageSize: number) => {
setQueryParams((prev) => ({ ...prev, page, pageSize })) setQueryParams((prev) => ({ ...prev, page, 'per-page': pageSize }))
} }
const pagination = { const pagination = {
......
import { upload } from '@/utils/upload'
import { PlusOutlined } from '@ant-design/icons'
import { Button, Upload } from 'antd'
export default function AppUpload({ value, onChange, ...props }: any) {
const generateFileList = (file: any) => {
if (!file || !file.name || !file.url) return []
return [
{
uid: Date.now().toString(),
name: file.name,
status: 'done',
url: file.url,
},
]
}
const fileList = generateFileList(value)
const [file] = fileList
// 自定义上传
const customRequest = async ({ onSuccess, onError, file }: any) => {
try {
const url = await upload(file)
onChange?.({ name: file.name, url })
onSuccess(url)
} catch (error) {
onError(error)
}
}
return (
<Upload customRequest={customRequest} fileList={fileList} showUploadList={false} maxCount={1} {...props}>
<Button type="primary" shape="circle" size="large" icon={<PlusOutlined />} />
<span style={{ marginLeft: '10px' }}>{file?.name}</span>
</Upload>
)
}
...@@ -4,10 +4,22 @@ import { CircleArrowLeft, CircleArrowRight } from 'lucide-react' ...@@ -4,10 +4,22 @@ import { CircleArrowLeft, CircleArrowRight } from 'lucide-react'
import './AIChat.scss' import './AIChat.scss'
import { OpenAIOutlined, SendOutlined } from '@ant-design/icons' import { OpenAIOutlined, SendOutlined } from '@ant-design/icons'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { useAI } from '@/hooks/useAI' import { useAI, AIMessage } from '@/hooks/useAI'
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
export const MessageItem = ({ message }: { message: AIMessage }) => {
return (
<div className={`message-item ${message.role}`}>
<div className="message-box">
<div className="message-content">
<Markdown remarkPlugins={[remarkGfm]}>{message.content}</Markdown>
</div>
</div>
</div>
)
}
export default function AIChat() { export default function AIChat() {
const [collapsed, setCollapsed] = useState(false) const [collapsed, setCollapsed] = useState(false)
...@@ -30,18 +42,6 @@ export default function AIChat() { ...@@ -30,18 +42,6 @@ export default function AIChat() {
post({ content }) post({ content })
} }
const messageItemRender = (message) => {
return (
<div className={`message-item ${message.role}`}>
<div className="message-box">
<div className="message-content">
<Markdown remarkPlugins={[remarkGfm]}>{message.content}</Markdown>
</div>
</div>
</div>
)
}
if (collapsed) { if (collapsed) {
return ( return (
<Card <Card
...@@ -51,7 +51,7 @@ export default function AIChat() { ...@@ -51,7 +51,7 @@ export default function AIChat() {
<div className="ai-chat-container"> <div className="ai-chat-container">
<div className="message-scroll"> <div className="message-scroll">
{messages.map((message) => { {messages.map((message) => {
return messageItemRender(message) return <MessageItem message={message}></MessageItem>
})} })}
</div> </div>
<Select value={ai} options={options} onChange={setAI} style={{ width: '100%' }}></Select> <Select value={ai} options={options} onChange={setAI} style={{ width: '100%' }}></Select>
......
...@@ -3,24 +3,35 @@ import md5 from 'blueimp-md5' ...@@ -3,24 +3,35 @@ import md5 from 'blueimp-md5'
import axios from 'axios' import axios from 'axios'
import { fetchEventSource } from '@fortaine/fetch-event-source' import { fetchEventSource } from '@fortaine/fetch-event-source'
export interface AIOption {
label: string
value: string
}
export interface AIMessage {
id?: string
role: 'user' | 'assistant'
content: string
}
export function useAI() { export function useAI() {
const options = [ const options: AIOption[] = [
{ label: '文心一言', value: 'yiyan' }, { label: '文心一言', value: 'yiyan' },
{ label: 'DeepSeek', value: 'deepseek' }, { label: 'DeepSeek', value: 'deepseek' },
{ label: '通义千问', value: 'qwen' }, { label: '通义千问', value: 'qwen' },
{ label: '天工', value: 'tiangong' }, { label: '天工', value: 'tiangong' },
] ]
const [ai, setAI] = useState(localStorage.getItem('ai') || 'yiyan') const [ai, setAI] = useState<string>(localStorage.getItem('ai') || 'yiyan')
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState<AIMessage[]>([])
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState<boolean>(false)
useEffect(() => { useEffect(() => {
localStorage.setItem('ai', ai) localStorage.setItem('ai', ai)
}, [ai]) }, [ai])
const post = useCallback( const post = useCallback(
async (data) => { async (data: { content: string }) => {
setIsLoading(true) setIsLoading(true)
setMessages((prevMessages) => [...prevMessages, { role: 'user', content: data.content }]) setMessages((prevMessages) => [...prevMessages, { role: 'user', content: data.content }])
try { try {
...@@ -46,10 +57,10 @@ export function useAI() { ...@@ -46,10 +57,10 @@ export function useAI() {
setIsLoading(false) setIsLoading(false)
} }
}, },
[ai] // 依赖 `ai`,当 `ai` 变化时,重新创建 `post` [ai]
) )
async function yiyan(data) { async function yiyan(data: any) {
const getAccessToken = async () => { const getAccessToken = async () => {
const AK = 'wY7bvMpkWeZbDVq9w3EDvpjU' const AK = 'wY7bvMpkWeZbDVq9w3EDvpjU'
const SK = 'XJwpiJWxs5HXkOtbo6tQrvYPZFJAWdAy' const SK = 'XJwpiJWxs5HXkOtbo6tQrvYPZFJAWdAy'
...@@ -67,7 +78,7 @@ export function useAI() { ...@@ -67,7 +78,7 @@ export function useAI() {
setMessages((prevMessages) => [...prevMessages, { role: 'assistant', content: resp.data.result }]) setMessages((prevMessages) => [...prevMessages, { role: 'assistant', content: resp.data.result }])
} }
async function deepseek(data) { async function deepseek(data: any) {
const apiKey = 'sk-f1a6f0a7013241de8393cb2cb108e777' const apiKey = 'sk-f1a6f0a7013241de8393cb2cb108e777'
const resp = await axios.post( const resp = await axios.post(
'/api/deepseek/chat/completions', '/api/deepseek/chat/completions',
...@@ -85,7 +96,7 @@ export function useAI() { ...@@ -85,7 +96,7 @@ export function useAI() {
} }
} }
async function qwen(data) { async function qwen(data: any) {
const apiKey = 'sk-afd0fcdb53bf4058b2068b8548820150' const apiKey = 'sk-afd0fcdb53bf4058b2068b8548820150'
const resp = await axios.post( const resp = await axios.post(
'/api/qwen/compatible-mode/v1/chat/completions', '/api/qwen/compatible-mode/v1/chat/completions',
...@@ -103,10 +114,10 @@ export function useAI() { ...@@ -103,10 +114,10 @@ export function useAI() {
} }
} }
async function tiangong(data) { async function tiangong(data: any) {
const appKey = 'a8701b73637562d33a53c668a90ee3be' const appKey = 'a8701b73637562d33a53c668a90ee3be'
const appSecret = 'e191593f486bb88a39c634f46926762dddc97b9082e192af' const appSecret = 'e191593f486bb88a39c634f46926762dddc97b9082e192af'
const timestamp = Math.floor(Date.now() / 1000) const timestamp = Math.floor(Date.now() / 1000).toString()
const sign = md5(`${appKey}${appSecret}${timestamp}`) const sign = md5(`${appKey}${appSecret}${timestamp}`)
return await fetchEventSource('/api/tiangong/sky-saas-writing/api/v1/chat', { return await fetchEventSource('/api/tiangong/sky-saas-writing/api/v1/chat', {
...@@ -116,14 +127,6 @@ export function useAI() { ...@@ -116,14 +127,6 @@ export function useAI() {
chat_history: [{ role: 'user', content: data.content }], chat_history: [{ role: 'user', content: data.content }],
stream_resp_type: 'update', stream_resp_type: 'update',
}), }),
async onopen(response) {
console.log(response)
if (response.ok) {
return response
} else {
throw response
}
},
onmessage(res) { onmessage(res) {
console.log(res.data) console.log(res.data)
const message = JSON.parse(res.data) const message = JSON.parse(res.data)
......
import httpRequest from '@/utils/axios'
import type { CreateDatasetParams } from './types'
// 创建内置数据集
export function createDataset(data: CreateDatasetParams) {
return httpRequest.post('/api/bi/v1/data/built-in/create', data)
}
// 删除内置数据集
export function deleteDataset(data: { id: string }) {
return httpRequest.post('/api/bi/v1/data/built-in/delete', data)
}
// 内置数据集列表
export function getDatasetList(params?: Partial<{ name: string; industry: string; page: number; 'per-page': number }>) {
return httpRequest.get('/api/bi/v1/data/built-in/list', { params })
}
import { Button, Form, Input, Modal, Select } from 'antd'
import { useMapStore } from '@/stores/map'
import AppUpload from '@/components/AppUpload'
import { useCreateDataset } from '../query'
import { useState } from 'react'
export default function FormButtonModal({ title, children }: { title: string; children?: React.ReactNode }) {
const [open, setOpen] = useState(false)
const [form] = Form.useForm()
const getMapValuesByKey = useMapStore((state) => state.getMapValuesByKey)
const industryList = getMapValuesByKey('bi_data_industry')
const dataSourceList = getMapValuesByKey('bi_data_source')
const sensitivityLevelList = getMapValuesByKey('bi_data_sensitivity_level')
const accessPermissionsList = getMapValuesByKey('bi_data_access_permissions')
const { mutate, isPending } = useCreateDataset()
const handleOk = () => {
form.validateFields().then((values) => {
const params = { ...values, file: JSON.stringify(values.file) }
mutate(params, {
onSuccess: () => setOpen(false),
})
})
}
return (
<>
{children ? (
children
) : (
<Button type="primary" onClick={() => setOpen(true)}>
{title}
</Button>
)}
<Modal
title={title}
open={open}
onOk={handleOk}
onCancel={() => setOpen(false)}
confirmLoading={isPending}
okText="保存"
destroyOnClose>
<Form form={form} labelCol={{ span: 5 }} preserve={false}>
<Form.Item label="数据集名称" name="name" rules={[{ required: true, message: '请输入数据集名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="所属行业" name="industry" rules={[{ required: true, message: '请选择所属行业' }]}>
<Select options={industryList}></Select>
</Form.Item>
<Form.Item label="数据来源" name="source" rules={[{ required: true, message: '请选择数据来源' }]}>
<Select options={dataSourceList}></Select>
</Form.Item>
<Form.Item label="敏感等级" name="sensitivity_level" rules={[{ required: true, message: '请选择敏感等级' }]}>
<Select options={sensitivityLevelList}></Select>
</Form.Item>
<Form.Item label="访问权限" name="access_permissions" rules={[{ required: true, message: '请选择访问权限' }]}>
<Select options={accessPermissionsList}></Select>
</Form.Item>
<Form.Item label="说明" name="describe">
<Input.TextArea rows={4} placeholder="请输入" />
</Form.Item>
<Form.Item name="file" label={null} rules={[{ required: true, message: '请上传文件' }]}>
<AppUpload accept=".xlsx,.csv" />
</Form.Item>
</Form>
</Modal>
</>
)
}
import { Form, Input, Modal, Select } from 'antd'
import { useMapStore } from '@/stores/map'
export default function FormModal(props) {
const getMapValuesByKey = useMapStore((state) => state.getMapValuesByKey)
const industryList = getMapValuesByKey('bi_data_industry')
const dataSourceList = getMapValuesByKey('bi_data_source')
const sensitivityLevelList = getMapValuesByKey('bi_data_sensitivity_level')
const accessPermissionsList = getMapValuesByKey('bi_data_access_permissions')
return (
<Modal title="添加数据集" {...props}>
<Form labelCol={{ span: 4 }}>
<Form.Item label="数据集名称" name="name">
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="所属行业" name="industry">
<Select options={industryList}></Select>
</Form.Item>
<Form.Item label="数据来源" name="data_source">
<Select options={dataSourceList}></Select>
</Form.Item>
<Form.Item label="敏感等级" name="level">
<Select options={sensitivityLevelList}></Select>
</Form.Item>
<Form.Item label="访问权限" name="access">
<Select options={accessPermissionsList}></Select>
</Form.Item>
<Form.Item label="说明" name="desc">
<Input.TextArea rows={4} placeholder="请输入" />
</Form.Item>
</Form>
</Modal>
)
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { createDataset, deleteDataset } from './api'
import type { CreateDatasetParams } from './types'
import { message } from 'antd'
// 创建
export function useCreateDataset() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: CreateDatasetParams) => createDataset(data),
onSuccess: () => {
message.success('创建成功')
queryClient.invalidateQueries({ queryKey: ['appList'] })
},
})
}
// 删除
export function useDeleteDataset() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: { id: string }) => deleteDataset(data),
onSuccess: () => {
message.success('删除成功')
queryClient.invalidateQueries({ queryKey: ['appList'] })
},
})
}
export interface CreateDatasetParams {
experiment_id: string
name: string
industry: string
source: string
sensitivity_level: string
access_permissions: string
describe: string
file: string
}
export interface DatasetListItem {
experiment_id: string
name: string
industry: string
source: string
sensitivity_level: string
access_permissions: string
describe: string
file: string
}
...@@ -2,28 +2,16 @@ import { lazy, useState } from 'react' ...@@ -2,28 +2,16 @@ import { lazy, useState } from 'react'
import { Button, Card, Input, Select } from 'antd' import { Button, Card, Input, Select } from 'antd'
import AppList, { AppListProps } from '@/components/AppList' import AppList, { AppListProps } from '@/components/AppList'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { getDatasetList } from '../api'
import { useDeleteDataset } from '../query'
const FormModal = lazy(() => import('../components/FormModal')) const FormButtonModal = lazy(() => import('../components/FormButtonModal'))
const ViewDataModal = lazy(() => import('@/components/data/ViewDataModal')) const ViewDataModal = lazy(() => import('@/components/data/ViewDataModal'))
export default function DataWriteBuilt() { export default function DataWriteBuilt() {
const getMapValuesByKey = useMapStore((state) => state.getMapValuesByKey) const getMapValuesByKey = useMapStore((state) => state.getMapValuesByKey)
const industryList = getMapValuesByKey('bi_data_industry') const industryList = getMapValuesByKey('bi_data_industry')
const [isModalOpen, setIsModalOpen] = useState(false)
const showModal = () => {
setIsModalOpen(true)
}
const handleOk = () => {
setIsModalOpen(false)
}
const handleCancel = () => {
setIsModalOpen(false)
}
const [viewModalIsOpen, setViewModalIsOpen] = useState(false) const [viewModalIsOpen, setViewModalIsOpen] = useState(false)
const handleView = (record: any) => { const handleView = (record: any) => {
...@@ -31,11 +19,16 @@ export default function DataWriteBuilt() { ...@@ -31,11 +19,16 @@ export default function DataWriteBuilt() {
setViewModalIsOpen(true) setViewModalIsOpen(true)
} }
const { mutate } = useDeleteDataset()
const handleRemove = (record: any) => { const handleRemove = (record: any) => {
console.log(record) mutate({ id: record.id })
} }
const listOptions: AppListProps = { const listOptions: AppListProps = {
fetchApi: async (params) => {
const { data } = await getDatasetList(params)
return { ...data }
},
filters: [ filters: [
{ {
name: 'industry', name: 'industry',
...@@ -56,16 +49,16 @@ export default function DataWriteBuilt() { ...@@ -56,16 +49,16 @@ export default function DataWriteBuilt() {
width: 62, width: 62,
align: 'center', align: 'center',
}, },
{ title: '数据集名称', dataIndex: 'name' }, { title: '数据集名称', dataIndex: 'name', align: 'center' },
{ title: '数据量', dataIndex: 'count' }, { title: '数据量', dataIndex: 'number', align: 'center' },
{ title: '所属行业', dataIndex: 'industry' }, { title: '所属行业', dataIndex: 'industry_name', align: 'center' },
{ title: '数据来源', dataIndex: 'sourc' }, { title: '数据来源', dataIndex: 'source_name', align: 'center' },
{ title: '敏感等级', dataIndex: 'level' }, { title: '敏感等级', dataIndex: 'sensitivity_level_name', align: 'center' },
{ title: '访问权限', dataIndex: 'auth' }, { title: '访问权限', dataIndex: 'access_permissions_name', align: 'center' },
{ title: '说明', dataIndex: 'desc' }, { title: '说明', dataIndex: 'describe', align: 'center' },
{ title: '创建人', dataIndex: 'create' }, { title: '创建人', dataIndex: 'created_operator_name', align: 'center' },
{ title: '创建时间', dataIndex: 'create_time' }, { title: '创建时间', dataIndex: 'created_time', align: 'center' },
{ title: '更新时间', dataIndex: 'update_time' }, { title: '更新时间', dataIndex: 'updated_time', align: 'center' },
{ {
title: '操作', title: '操作',
key: 'x', key: 'x',
...@@ -93,16 +86,7 @@ export default function DataWriteBuilt() { ...@@ -93,16 +86,7 @@ export default function DataWriteBuilt() {
} }
return ( return (
<Card className="app-card" title="内置数据集管理"> <Card className="app-card" title="内置数据集管理">
<AppList <AppList bordered {...listOptions} filterAside={<FormButtonModal title="添加数据集"></FormButtonModal>}></AppList>
bordered
{...listOptions}
filterAside={
<Button type="primary" onClick={showModal}>
添加数据集
</Button>
}></AppList>
<FormModal open={isModalOpen} onOk={handleOk} onCancel={handleCancel}></FormModal>
<ViewDataModal open={viewModalIsOpen} onCancel={() => setViewModalIsOpen(false)}></ViewDataModal> <ViewDataModal open={viewModalIsOpen} onCancel={() => setViewModalIsOpen(false)}></ViewDataModal>
</Card> </Card>
......
...@@ -45,12 +45,10 @@ export default function DataWriteUpload() { ...@@ -45,12 +45,10 @@ export default function DataWriteUpload() {
</Form.Item> </Form.Item>
</Form> </Form>
<Flex align="center" gap={100} style={{ margin: '10px 0 30px' }}> <Flex align="center" gap={100} style={{ margin: '10px 0 30px' }}>
<div> <Upload {...props}>
<Upload {...props}> <Button type="primary" shape="circle" size="large" icon={<PlusOutlined />}></Button>
<Button type="primary" shape="circle" size="large" icon={<PlusOutlined />}></Button>
</Upload>
<span style={{ marginLeft: '10px' }}>{file?.name}</span> <span style={{ marginLeft: '10px' }}>{file?.name}</span>
</div> </Upload>
<p>共计:{data.length}条数据</p> <p>共计:{data.length}条数据</p>
<Button type="primary">保存</Button> <Button type="primary">保存</Button>
</Flex> </Flex>
......
...@@ -9,6 +9,28 @@ const axiosInstance = axios.create({ ...@@ -9,6 +9,28 @@ const axiosInstance = axios.create({
// }, // },
}) })
// 请求拦截
axiosInstance.interceptors.request.use(
function (config) {
const params = new URLSearchParams(window.location.search)
if (config.method === 'get')
config.params = Object.assign({}, config.params, {
experiment_id: params.get('experiment_id') || '7028276368903241728',
})
if (config.method === 'post')
config.data = Object.assign({}, config.data, {
experiment_id: params.get('experiment_id') || '7028276368903241728',
})
return config
},
function (error) {
return Promise.reject(error)
}
)
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
(response) => { (response) => {
const { data } = response const { data } = response
......
import axios from 'axios'
import md5 from 'blueimp-md5'
import { getSignature, uploadFile } from '@/api/base'
export async function upload(blob: Blob | File) {
let fileType = 'png'
if (blob instanceof File && blob.name) {
const matches = blob.name.match(/\.(\w+)$/)
if (matches) {
fileType = matches[1]
}
} else if (blob.type) {
const mimeType = blob.type.split('/').pop()
if (mimeType) {
fileType = mimeType
}
}
const key = 'upload/saas-bi/' + md5(new Date().getTime() + Math.random().toString(36).slice(-8)) + '.' + fileType
const response: any = await getSignature()
const params = {
key,
host: response.host,
OSSAccessKeyId: response.accessid,
policy: response.policy,
signature: response.signature,
success_action_status: '200',
file: blob,
url: `${response.host}/${key}`,
}
await uploadFile(params)
return params.url
}
export async function uploadFileByUrl(url: string) {
const res = await axios.get(url, { responseType: 'blob' })
return upload(res.data)
}
...@@ -10,6 +10,11 @@ export default defineConfig({ ...@@ -10,6 +10,11 @@ export default defineConfig({
open: true, open: true,
host: 'dev.ezijing.com', host: 'dev.ezijing.com',
proxy: { proxy: {
'/api/bi': {
target: 'http://local-com-resource-api.bi.ezijing.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/bi/, ''),
},
'/api': 'https://saas-lab.ezijing.com', '/api': 'https://saas-lab.ezijing.com',
}, },
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论