From 449d1191ff8212555fc9851ac9611f1da5d1b1d4 Mon Sep 17 00:00:00 2001 From: scholar <1145227973@qq.com> Date: Sat, 4 May 2024 22:28:37 +0800 Subject: [PATCH 001/235] =?UTF-8?q?fix=E5=90=8C=E6=AF=94=E7=8E=AF=E6=AF=94?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=AE=A1=E7=AE=97=E7=9A=84bug=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ContractCountPerformance.vue | 14 +++++++------- .../components/ContractPricePerformance.vue | 14 +++++++------- .../components/ReceivablePricePerformance.vue | 14 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/views/crm/statistics/performance/components/ContractCountPerformance.vue b/src/views/crm/statistics/performance/components/ContractCountPerformance.vue index f135caa8..5dc4d9e7 100644 --- a/src/views/crm/statistics/performance/components/ContractCountPerformance.vue +++ b/src/views/crm/statistics/performance/components/ContractCountPerformance.vue @@ -57,13 +57,13 @@ const echartsOption = reactive({ data: [] }, { - name: '同比增长率(%)', + name: '环比增长率(%)', type: 'line', yAxisIndex: 1, data: [] }, { - name: '环比增长率(%)', + name: '同比增长率(%)', type: 'line', yAxisIndex: 1, data: [] @@ -166,7 +166,7 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) echartsOption.series[3]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? (s.currentMonthCount / s.lastMonthCount*100).toFixed(2) : 'NULL' + (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? ((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount*100).toFixed(2) : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { @@ -174,7 +174,7 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastYearCount ) echartsOption.series[4]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? (s.currentMonthCount / s.lastYearCount*100).toFixed(2) : 'NULL' + (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? ((s.currentMonthCount - s.lastYearCount) / s.lastYearCount*100).toFixed(2) : 'NULL' ) } @@ -188,7 +188,7 @@ const loadData = async () => { // 初始化数据 const columnsData = reactive([]); const tableData = reactive([{title: '当月合同数量统计(个)'}, {title: '上月合同数量统计(个)'}, - {title: '去年当月合同数量统计(个)'}, {title: '同比增长率(%)'}, {title: '环比增长率(%)'}]) + {title: '去年当月合同数量统计(个)'}, {title: '环比增长率(%)'}, {title: '同比增长率(%)'}]) // 定义 convertListData 方法,数据行列转置,展示每月数据 const convertListData = () => { @@ -202,8 +202,8 @@ const convertListData = () => { tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? (item.currentMonthCount / item.lastMonthCount).toFixed(2) : 'NULL' - tableData[4]['prop' + index] = item.lastYearCount !== 0 ? (item.currentMonthCount / item.lastYearCount).toFixed(2) : 'NULL' + tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? ((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount * 100).toFixed(2) : 'NULL' + tableData[4]['prop' + index] = item.lastYearCount !== 0 ? ((item.currentMonthCount - item.lastYearCount) / item.lastYearCount * 100).toFixed(2) : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/components/ContractPricePerformance.vue b/src/views/crm/statistics/performance/components/ContractPricePerformance.vue index 41228a22..0c33dc22 100644 --- a/src/views/crm/statistics/performance/components/ContractPricePerformance.vue +++ b/src/views/crm/statistics/performance/components/ContractPricePerformance.vue @@ -57,13 +57,13 @@ const echartsOption = reactive({ data: [] }, { - name: '同比增长率(%)', + name: '环比增长率(%)', type: 'line', yAxisIndex: 1, data: [] }, { - name: '环比增长率(%)', + name: '同比增长率(%)', type: 'line', yAxisIndex: 1, data: [] @@ -166,7 +166,7 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) echartsOption.series[3]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? (s.currentMonthCount / s.lastMonthCount*100).toFixed(2) : 'NULL' + (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? ((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount*100).toFixed(2) : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { @@ -174,7 +174,7 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastYearCount ) echartsOption.series[4]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? (s.currentMonthCount / s.lastYearCount*100).toFixed(2) : 'NULL' + (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? ((s.currentMonthCount - s.lastYearCount) / s.lastYearCount*100).toFixed(2) : 'NULL' ) } @@ -188,7 +188,7 @@ const loadData = async () => { // 初始化数据 const columnsData = reactive([]); const tableData = reactive([{title: '当月合同金额统计(元)'}, {title: '上月合同金额统计(元)'}, {title: '去年当月合同金额统计(元)'}, - {title: '同比增长率(%)'}, {title: '环比增长率(%)'}]) + {title: '环比增长率(%)'}, {title: '同比增长率(%)'}]) // 定义 init 方法 const convertListData = () => { @@ -202,8 +202,8 @@ const convertListData = () => { tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? (item.currentMonthCount / item.lastMonthCount).toFixed(2) : 'NULL' - tableData[4]['prop' + index] = item.lastYearCount !== 0 ? (item.currentMonthCount / item.lastYearCount).toFixed(2) : 'NULL' + tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? ((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount*100).toFixed(2) : 'NULL' + tableData[4]['prop' + index] = item.lastYearCount !== 0 ? ((item.currentMonthCount - item.lastYearCount) / item.lastYearCount*100).toFixed(2) : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue b/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue index 1efcf920..aa3f5b19 100644 --- a/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue +++ b/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue @@ -57,13 +57,13 @@ const echartsOption = reactive({ data: [] }, { - name: '同比增长率(%)', + name: '环比增长率(%)', type: 'line', yAxisIndex: 1, data: [] }, { - name: '环比增长率(%)', + name: '同比增长率(%)', type: 'line', yAxisIndex: 1, data: [] @@ -166,7 +166,7 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) echartsOption.series[3]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? (s.currentMonthCount / s.lastMonthCount*100).toFixed(2) : 'NULL' + (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? ((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount*100).toFixed(2) : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[1]['data']) { @@ -174,7 +174,7 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastYearCount ) echartsOption.series[4]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? (s.currentMonthCount / s.lastYearCount*100).toFixed(2) : 'NULL' + (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? ((s.currentMonthCount - s.lastYearCount) / s.lastYearCount*100).toFixed(2) : 'NULL' ) } @@ -188,7 +188,7 @@ const loadData = async () => { // 初始化数据 const columnsData = reactive([]); const tableData = reactive([{title: '当月回款金额统计(元)'}, {title: '上月回款金额统计(元)'}, - {title: '去年当月回款金额统计(元)'}, {title: '同比增长率(%)'}, {title: '环比增长率(%)'}]); + {title: '去年当月回款金额统计(元)'}, {title: '环比增长率(%)'}, {title: '同比增长率(%)'}]); // 定义 init 方法 const convertListData = () => { @@ -202,8 +202,8 @@ const convertListData = () => { tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? (item.currentMonthCount / item.lastMonthCount).toFixed(2) : 'NULL' - tableData[4]['prop' + index] = item.lastYearCount !== 0 ? (item.currentMonthCount / item.lastYearCount).toFixed(2) : 'NULL' + tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? ((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount*100).toFixed(2) : 'NULL' + tableData[4]['prop' + index] = item.lastYearCount !== 0 ? ((item.currentMonthCount - item.lastYearCount) / item.lastYearCount*100).toFixed(2) : 'NULL' }) } From 40fe87920b075e91ff04e1215e320974b7d25106 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 9 May 2024 14:35:57 +0800 Subject: [PATCH 002/235] =?UTF-8?q?form-create:=20=E4=BC=98=E5=8C=96=20sel?= =?UTF-8?q?ect=20option=20=E9=80=89=E9=A1=B9=E8=A7=A3=E6=9E=90=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20el=20=E8=A1=A8=E8=BE=BE=E5=BC=8F=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E3=80=81=E8=87=AA=E5=AE=9A=E4=B9=89=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=20(data:=20any)=3D>{=20label:=20string;=20va?= =?UTF-8?q?lue:=20any=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/useApiSelect.tsx | 41 +++++++++++++++++-- .../FormCreate/src/config/selectRule.ts | 13 +++++- src/components/FormCreate/src/utils/index.ts | 1 - 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/components/FormCreate/src/components/useApiSelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx index 54c0a33b..62f25eb3 100644 --- a/src/components/FormCreate/src/components/useApiSelect.tsx +++ b/src/components/FormCreate/src/components/useApiSelect.tsx @@ -27,6 +27,11 @@ export const useApiSelect = (option: ApiSelectProps) => { type: String, default: 'GET' }, + // 选项解析函数 + parseFunc: { + type: String, + default: '' + }, // 请求参数 data: { type: String, @@ -63,15 +68,43 @@ export const useApiSelect = (option: ApiSelectProps) => { } if (Array.isArray(data)) { - options.value = data.map((item: any) => ({ - label: item[props.labelField], - value: item[props.valueField] - })) + let parse: any = null + if (!!props.parseFunc) { + // 解析字符串函数 + parse = new Function(`return ${props.parseFunc}`)() + } + options.value = data.map( + (item: any) => + parse?.(item) ?? { + label: parseExpression(item, props.labelField), + value: parseExpression(item, props.valueField) + } + ) return } console.log(`接口[${props.url}] 返回结果不是一个数组`) } + function parseExpression(data: any, template: string) { + // 检测是否使用了表达式 + if (template.indexOf('${') === -1) { + return data[template] + } + // 正则表达式匹配模板字符串中的 ${...} + const pattern = /\$\{([^}]*)}/g + // 使用replace函数配合正则表达式和回调函数来进行替换 + return template.replace(pattern, (_, expr) => { + // expr 是匹配到的 ${} 内的表达式(这里是属性名),从 data 中获取对应的值 + const result = data[expr.trim()] // 去除前后空白,以防用户输入带空格的属性名 + if (!result) { + console.warn( + `接口选择器选项模版[${template}][${expr.trim()}] 解析值失败结果为[${result}], 请检查属性名称是否存在于接口返回值中,存在则忽略此条!!!` + ) + } + return result + }) + } + onMounted(async () => { await getOptions() }) diff --git a/src/components/FormCreate/src/config/selectRule.ts b/src/components/FormCreate/src/config/selectRule.ts index 281d3739..ec5af7c7 100644 --- a/src/components/FormCreate/src/config/selectRule.ts +++ b/src/components/FormCreate/src/config/selectRule.ts @@ -13,7 +13,7 @@ const selectRule = [ control: [ { value: 'select', - condition: '=', + condition: '==', method: 'hidden', rule: ['multiple'] } @@ -141,6 +141,17 @@ const apiSelectRule = [ props: { placeholder: 'id' } + }, + { + type: 'input', + field: 'parseFunc', + title: '选项解析函数', + info: '(data: any)=>{ label: string; value: any }', + props: { + autosize: true, + rows: { minRows: 2, maxRows: 6 }, + type: 'textarea' + } } ] diff --git a/src/components/FormCreate/src/utils/index.ts b/src/components/FormCreate/src/utils/index.ts index e5480981..c297e841 100644 --- a/src/components/FormCreate/src/utils/index.ts +++ b/src/components/FormCreate/src/utils/index.ts @@ -1,4 +1,3 @@ -// TODO puhui999: 借鉴一下 form-create-designer utils 方法 🤣 (导入不了只能先 copy 过来用下) export function makeRequiredRule() { return { type: 'Required', From 9cde4885777576a145c06253ff634cef63c1fdd3 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 9 May 2024 17:30:49 +0800 Subject: [PATCH 003/235] =?UTF-8?q?form-create:=20=E5=AE=8C=E5=96=84=20sel?= =?UTF-8?q?ect=20=E8=BF=9C=E7=A8=8B=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/useApiSelect.tsx | 110 +++++++++++++++--- .../FormCreate/src/config/selectRule.ts | 34 +++++- .../src/config/useDictSelectRule.ts | 4 +- .../FormCreate/src/config/useSelectRule.ts | 4 +- src/components/FormCreate/src/utils/index.ts | 60 ---------- 5 files changed, 125 insertions(+), 87 deletions(-) diff --git a/src/components/FormCreate/src/components/useApiSelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx index 62f25eb3..29cd3027 100644 --- a/src/components/FormCreate/src/components/useApiSelect.tsx +++ b/src/components/FormCreate/src/components/useApiSelect.tsx @@ -46,43 +46,88 @@ export const useApiSelect = (option: ApiSelectProps) => { multiple: { type: Boolean, default: false + }, + // 是否远程搜索 + remote: { + type: Boolean, + default: false + }, + // 远程搜索时携带的参数 + remoteField: { + type: String, + default: 'label' } }, setup(props) { const attrs = useAttrs() const options = ref([]) // 下拉数据 + const loading = ref(false) // 是否正在从远程获取数据 + const queryParam = ref() // 当前输入的值 const getOptions = async () => { options.value = [] // 接口选择器 if (isEmpty(props.url)) { return } - let data = [] switch (props.method) { case 'GET': - data = await request.get({ url: props.url }) + let url: string = props.url + if (props.remote) { + url = `${url}?${props.remoteField}=${queryParam.value}` + } + parseOptions(await request.get({ url: url })) break case 'POST': - data = await request.post({ url: props.url, data: jsonParse(props.data) }) + const data: any = jsonParse(props.data) + if (props.remote) { + data[props.remoteField] = queryParam.value + } + parseOptions(await request.post({ url: props.url, data: data })) break } + } - if (Array.isArray(data)) { - let parse: any = null - if (!!props.parseFunc) { - // 解析字符串函数 - parse = new Function(`return ${props.parseFunc}`)() - } - options.value = data.map( - (item: any) => - parse?.(item) ?? { - label: parseExpression(item, props.labelField), - value: parseExpression(item, props.valueField) - } - ) + function parseOptions(data: any) { + // 情况一:如果有自定义解析函数优先使用自定义解析 + if (!isEmpty(props.parseFunc)) { + options.value = parseFunc()?.(data) return } - console.log(`接口[${props.url}] 返回结果不是一个数组`) + // 情况二:返回的直接是一个列表 + if (Array.isArray(data)) { + parseOptions0(data) + return + } + // 情况二:返回的是分页数据,尝试读取 list + data = data.list + if (!!data && Array.isArray(data)) { + parseOptions0(data) + return + } + // 情况三:不是 yudao-vue-pro 标准返回 + console.warn( + `接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理` + ) + } + + function parseOptions0(data: any[]) { + if (Array.isArray(data)) { + options.value = data.map((item: any) => ({ + label: parseExpression(item, props.labelField), + value: parseExpression(item, props.valueField) + })) + return + } + console.warn(`接口[${props.url}] 返回结果不是一个数组`) + } + + function parseFunc() { + let parse: any = null + if (!!props.parseFunc) { + // 解析字符串函数 + parse = new Function(`return ${props.parseFunc}`)() + } + return parse } function parseExpression(data: any, template: string) { @@ -105,6 +150,19 @@ export const useApiSelect = (option: ApiSelectProps) => { }) } + const remoteMethod = async (query: any) => { + if (!query) { + return + } + loading.value = true + try { + queryParam.value = query + await getOptions() + } finally { + loading.value = false + } + } + onMounted(async () => { await getOptions() }) @@ -113,15 +171,29 @@ export const useApiSelect = (option: ApiSelectProps) => { if (props.multiple) { // fix:多写此步是为了解决 multiple 属性问题 return ( - + {options.value.map((item, index) => ( ))} ) } + debugger return ( - + {options.value.map((item, index) => ( ))} diff --git a/src/components/FormCreate/src/config/selectRule.ts b/src/components/FormCreate/src/config/selectRule.ts index ec5af7c7..5d24c713 100644 --- a/src/components/FormCreate/src/config/selectRule.ts +++ b/src/components/FormCreate/src/config/selectRule.ts @@ -15,7 +15,20 @@ const selectRule = [ value: 'select', condition: '==', method: 'hidden', - rule: ['multiple'] + rule: [ + 'multiple', + 'clearable', + 'collapseTags', + 'multipleLimit', + 'allowCreate', + 'filterable', + 'noMatchText', + 'remote', + 'remoteMethod', + 'reserveKeyword', + 'defaultFirstOption', + 'automaticDropdown' + ] } ] }, @@ -60,9 +73,10 @@ const selectRule = [ title: '其中的选项是否从服务器远程加载' }, { - type: 'Struct', - field: 'remoteMethod', - title: '自定义远程搜索方法' + type: 'input', + field: 'remoteField', + title: '请求参数', + info: '远程请求时请求携带的参数名称,如:name' }, { type: 'input', field: 'noDataText', title: '选项为空时显示的文字' }, { @@ -130,6 +144,7 @@ const apiSelectRule = [ type: 'input', field: 'labelField', title: 'label 属性', + info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', props: { placeholder: 'nickname' } @@ -138,6 +153,7 @@ const apiSelectRule = [ type: 'input', field: 'valueField', title: 'value 属性', + info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', props: { placeholder: 'id' } @@ -146,11 +162,17 @@ const apiSelectRule = [ type: 'input', field: 'parseFunc', title: '选项解析函数', - info: '(data: any)=>{ label: string; value: any }', + info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表 + (data: any)=>{ label: string; value: any }[]`, props: { autosize: true, rows: { minRows: 2, maxRows: 6 }, - type: 'textarea' + type: 'textarea', + placeholder: ` + function (data) { + console.log(data) + return data.list.map(item=> ({label: item.nickname,value: item.id})) + }` } } ] diff --git a/src/components/FormCreate/src/config/useDictSelectRule.ts b/src/components/FormCreate/src/config/useDictSelectRule.ts index 3db630bc..5c5e8cad 100644 --- a/src/components/FormCreate/src/config/useDictSelectRule.ts +++ b/src/components/FormCreate/src/config/useDictSelectRule.ts @@ -2,6 +2,7 @@ import { generateUUID } from '@/utils' import * as DictDataApi from '@/api/system/dict/dict.type' import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' import { selectRule } from '@/components/FormCreate/src/config/selectRule' +import { cloneDeep } from 'lodash-es' /** * 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule @@ -9,6 +10,7 @@ import { selectRule } from '@/components/FormCreate/src/config/selectRule' export const useDictSelectRule = () => { const label = '字典选择器' const name = 'DictSelect' + const rules = cloneDeep(selectRule) const dictOptions = ref<{ label: string; value: string }[]>([]) // 字典类型下拉数据 onMounted(async () => { const data = await DictDataApi.getSimpleDictTypeList() @@ -55,7 +57,7 @@ export const useDictSelectRule = () => { { label: '布尔值', value: 'bool' } ] }, - ...selectRule + ...rules ]) } } diff --git a/src/components/FormCreate/src/config/useSelectRule.ts b/src/components/FormCreate/src/config/useSelectRule.ts index 732c5269..3e5f1eb2 100644 --- a/src/components/FormCreate/src/config/useSelectRule.ts +++ b/src/components/FormCreate/src/config/useSelectRule.ts @@ -2,6 +2,7 @@ import { generateUUID } from '@/utils' import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' import { selectRule } from '@/components/FormCreate/src/config/selectRule' import { SelectRuleOption } from '@/components/FormCreate/src/type' +import { cloneDeep } from 'lodash-es' /** * 通用选择器规则 hook @@ -10,6 +11,7 @@ import { SelectRuleOption } from '@/components/FormCreate/src/type' export const useSelectRule = (option: SelectRuleOption) => { const label = option.label const name = option.name + const rules = cloneDeep(selectRule) return { icon: option.icon, label, @@ -27,7 +29,7 @@ export const useSelectRule = (option: SelectRuleOption) => { if (!option.props) { option.props = [] } - return localeProps(t, name + '.props', [makeRequiredRule(), ...option.props, ...selectRule]) + return localeProps(t, name + '.props', [makeRequiredRule(), ...option.props, ...rules]) } } } diff --git a/src/components/FormCreate/src/utils/index.ts b/src/components/FormCreate/src/utils/index.ts index c297e841..2d4a6fd7 100644 --- a/src/components/FormCreate/src/utils/index.ts +++ b/src/components/FormCreate/src/utils/index.ts @@ -16,63 +16,3 @@ export const localeProps = (t, prefix, rules) => { return rule }) } - -export function upper(str) { - return str.replace(str[0], str[0].toLocaleUpperCase()) -} - -export function makeOptionsRule(t, to, userOptions) { - console.log(userOptions[0]) - const options = [ - { label: t('props.optionsType.struct'), value: 0 }, - { label: t('props.optionsType.json'), value: 1 }, - { label: '用户数据', value: 2 } - ] - - const control = [ - { - value: 0, - rule: [ - { - type: 'TableOptions', - field: 'formCreate' + upper(to).replace('.', '>'), - props: { defaultValue: [] } - } - ] - }, - { - value: 1, - rule: [ - { - type: 'Struct', - field: 'formCreate' + upper(to).replace('.', '>'), - props: { defaultValue: [] } - } - ] - }, - { - value: 2, - rule: [ - { - type: 'TableOptions', - field: 'formCreate' + upper(to).replace('.', '>'), - props: { modelValue: [] } - } - ] - } - ] - options.splice(0, 0) - control.push() - - return { - type: 'radio', - title: t('props.options'), - field: '_optionType', - value: 0, - options, - props: { - type: 'button' - }, - control - } -} From 24f2c49e0b04c4e73239ffc11524ed34462b7425 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 9 May 2024 18:02:57 +0800 Subject: [PATCH 004/235] =?UTF-8?q?form-create:=20=E5=AE=8C=E5=96=84=20sel?= =?UTF-8?q?ect=20=E8=BF=9C=E7=A8=8B=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FormCreate/src/config/selectRule.ts | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/components/FormCreate/src/config/selectRule.ts b/src/components/FormCreate/src/config/selectRule.ts index 5d24c713..a6f3841f 100644 --- a/src/components/FormCreate/src/config/selectRule.ts +++ b/src/components/FormCreate/src/config/selectRule.ts @@ -32,6 +32,11 @@ const selectRule = [ } ] }, + { + type: 'switch', + field: 'filterable', + title: '是否可搜索' + }, { type: 'switch', field: 'multiple', title: '是否多选' }, { type: 'switch', @@ -56,28 +61,12 @@ const selectRule = [ title: 'autocomplete 属性' }, { type: 'input', field: 'placeholder', title: '占位符' }, - { - type: 'switch', - field: 'filterable', - title: '是否可搜索' - }, { type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' }, { type: 'input', field: 'noMatchText', title: '搜索条件无匹配时显示的文字' }, - { - type: 'switch', - field: 'remote', - title: '其中的选项是否从服务器远程加载' - }, - { - type: 'input', - field: 'remoteField', - title: '请求参数', - info: '远程请求时请求携带的参数名称,如:name' - }, { type: 'input', field: 'noDataText', title: '选项为空时显示的文字' }, { type: 'switch', @@ -174,6 +163,18 @@ const apiSelectRule = [ return data.list.map(item=> ({label: item.nickname,value: item.id})) }` } + }, + { + type: 'switch', + field: 'remote', + info: '是否可搜索', + title: '其中的选项是否从服务器远程加载' + }, + { + type: 'input', + field: 'remoteField', + title: '请求参数', + info: '远程请求时请求携带的参数名称,如:name' } ] From 5525bc2257c7cbb7852b02143a9edee0a55bb2b9 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Thu, 9 May 2024 22:13:23 +0800 Subject: [PATCH 005/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E4=BC=98=E5=8C=96=20ai=20chat=20=E4=B8=BB=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/index.vue | 228 +++++++++++++++++++++++++----------- 1 file changed, 160 insertions(+), 68 deletions(-) diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index bc846a3d..ec49b774 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -1,67 +1,92 @@ @@ -105,21 +130,60 @@ const searchConversation = () => { } From 02fe7e31d058ab111f9c55d0dd53175749097e6c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 9 May 2024 23:02:57 +0800 Subject: [PATCH 006/235] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9AAPI=20=E5=AF=86=E9=92=A5=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ai/model/apiKey/index.ts | 39 +++++ src/permission.ts | 2 +- src/utils/dict.ts | 9 +- src/views/ai/model/apiKey/ApiKeyForm.vue | 132 +++++++++++++++++ src/views/ai/model/apiKey/index.vue | 181 +++++++++++++++++++++++ 5 files changed, 359 insertions(+), 4 deletions(-) create mode 100644 src/api/ai/model/apiKey/index.ts create mode 100644 src/views/ai/model/apiKey/ApiKeyForm.vue create mode 100644 src/views/ai/model/apiKey/index.vue diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts new file mode 100644 index 00000000..47e415e4 --- /dev/null +++ b/src/api/ai/model/apiKey/index.ts @@ -0,0 +1,39 @@ +import request from '@/config/axios' + +// AI API 密钥 VO +export interface ApiKeyVO { + id: number // 编号 + name: string // 名称 + apiKey: string // 密钥 + platform: string // 平台 + url: string // 自定义 API 地址 + status: number // 状态 +} + +// AI API 密钥 API +export const ApiKeyApi = { + // 查询AI API 密钥分页 + getApiKeyPage: async (params: any) => { + return await request.get({ url: `/ai/api-key/page`, params }) + }, + + // 查询AI API 密钥详情 + getApiKey: async (id: number) => { + return await request.get({ url: `/ai/api-key/get?id=` + id }) + }, + + // 新增AI API 密钥 + createApiKey: async (data: ApiKeyVO) => { + return await request.post({ url: `/ai/api-key/create`, data }) + }, + + // 修改AI API 密钥 + updateApiKey: async (data: ApiKeyVO) => { + return await request.put({ url: `/ai/api-key/update`, data }) + }, + + // 删除AI API 密钥 + deleteApiKey: async (id: number) => { + return await request.delete({ url: `/ai/api-key/delete?id=` + id }) + } +} diff --git a/src/permission.ts b/src/permission.ts index d538303b..b04bc3c1 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -83,7 +83,7 @@ router.beforeEach(async (to, from, next) => { const redirectPath = from.query.redirect || to.path // 修复跳转时不带参数的问题 const redirect = decodeURIComponent(redirectPath as string) - const { basePath, paramsObject: query } = parseURL(redirect) + const { paramsObject: query } = parseURL(redirect) const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect, query } next(nextData) } else { diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 631a40b0..ab6e2923 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -1,8 +1,8 @@ /** * 数据字典工具类 */ -import {useDictStoreWithOut} from '@/store/modules/dict' -import {ElementPlusInfoType} from '@/types/elementPlus' +import { useDictStoreWithOut } from '@/store/modules/dict' +import { ElementPlusInfoType } from '@/types/elementPlus' const dictStore = useDictStoreWithOut() @@ -209,5 +209,8 @@ export enum DICT_TYPE { // ========== ERP - 企业资源计划模块 ========== ERP_AUDIT_STATUS = 'erp_audit_status', // ERP 审批状态 - ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type' // 库存明细的业务类型 + ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type', // 库存明细的业务类型 + + // ========== AI - 人工智能模块 ========== + AI_PLATFORM = 'ai_platform' // AI 平台 } diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue new file mode 100644 index 00000000..2f9ba580 --- /dev/null +++ b/src/views/ai/model/apiKey/ApiKeyForm.vue @@ -0,0 +1,132 @@ + + diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue new file mode 100644 index 00000000..fcafc463 --- /dev/null +++ b/src/views/ai/model/apiKey/index.vue @@ -0,0 +1,181 @@ + + + From 1776217f1c861c65e15f3c48ff8b14c489b6d70f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 10 May 2024 22:48:33 +0800 Subject: [PATCH 007/235] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9AAPI=20=E6=A8=A1=E5=9E=8B=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ai/model/apiKey/index.ts | 10 +- src/api/ai/model/chatModel/index.ts | 43 +++++ .../ai/model/chatModel/ChatModelForm.vue | 140 ++++++++++++++ src/views/ai/model/chatModel/index.vue | 179 ++++++++++++++++++ 4 files changed, 367 insertions(+), 5 deletions(-) create mode 100644 src/api/ai/model/chatModel/index.ts create mode 100644 src/views/ai/model/chatModel/ChatModelForm.vue create mode 100644 src/views/ai/model/chatModel/index.vue diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts index 47e415e4..c96f2a66 100644 --- a/src/api/ai/model/apiKey/index.ts +++ b/src/api/ai/model/apiKey/index.ts @@ -12,27 +12,27 @@ export interface ApiKeyVO { // AI API 密钥 API export const ApiKeyApi = { - // 查询AI API 密钥分页 + // 查询 API 密钥分页 getApiKeyPage: async (params: any) => { return await request.get({ url: `/ai/api-key/page`, params }) }, - // 查询AI API 密钥详情 + // 查询 API 密钥详情 getApiKey: async (id: number) => { return await request.get({ url: `/ai/api-key/get?id=` + id }) }, - // 新增AI API 密钥 + // 新增 API 密钥 createApiKey: async (data: ApiKeyVO) => { return await request.post({ url: `/ai/api-key/create`, data }) }, - // 修改AI API 密钥 + // 修改 API 密钥 updateApiKey: async (data: ApiKeyVO) => { return await request.put({ url: `/ai/api-key/update`, data }) }, - // 删除AI API 密钥 + // 删除 API 密钥 deleteApiKey: async (id: number) => { return await request.delete({ url: `/ai/api-key/delete?id=` + id }) } diff --git a/src/api/ai/model/chatModel/index.ts b/src/api/ai/model/chatModel/index.ts new file mode 100644 index 00000000..09f8d66b --- /dev/null +++ b/src/api/ai/model/chatModel/index.ts @@ -0,0 +1,43 @@ +import request from '@/config/axios' + +// AI 聊天模型 VO +export interface ChatModelVO { + id: number // 编号 + keyId: number // API 秘钥编号 + name: string // 模型名字 + model: string // 模型标识 + platform: string // 模型平台 + sort: number // 排序 + status: number // 状态 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 +} + +// AI 聊天模型 API +export const ChatModelApi = { + // 查询聊天模型分页 + getChatModelPage: async (params: any) => { + return await request.get({ url: `/ai/chat-model/page`, params }) + }, + + // 查询聊天模型详情 + getChatModel: async (id: number) => { + return await request.get({ url: `/ai/chat-model/get?id=` + id }) + }, + + // 新增聊天模型 + createChatModel: async (data: ChatModelVO) => { + return await request.post({ url: `/ai/chat-model/create`, data }) + }, + + // 修改聊天模型 + updateChatModel: async (data: ChatModelVO) => { + return await request.put({ url: `/ai/chat-model/update`, data }) + }, + + // 删除聊天模型 + deleteChatModel: async (id: number) => { + return await request.delete({ url: `/ai/chat-model/delete?id=` + id }) + } +} diff --git a/src/views/ai/model/chatModel/ChatModelForm.vue b/src/views/ai/model/chatModel/ChatModelForm.vue new file mode 100644 index 00000000..65374291 --- /dev/null +++ b/src/views/ai/model/chatModel/ChatModelForm.vue @@ -0,0 +1,140 @@ + + diff --git a/src/views/ai/model/chatModel/index.vue b/src/views/ai/model/chatModel/index.vue new file mode 100644 index 00000000..d43051aa --- /dev/null +++ b/src/views/ai/model/chatModel/index.vue @@ -0,0 +1,179 @@ + + + From 9d322c3a94616d00bd1511828f3f581dd33c938e Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 10 May 2024 23:35:47 +0800 Subject: [PATCH 008/235] =?UTF-8?q?=E3=80=90=E6=B7=BB=E5=8A=A0=E3=80=91?= =?UTF-8?q?=E8=B0=83=E6=95=B4ui=20=E5=A2=9E=E5=8A=A0=20chat=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E5=92=8C=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/assets/ai/copy.svg | 1 + src/assets/ai/delete.svg | 1 + src/views/ai/chat/index.vue | 254 ++++++++++++++++++++++++++++++++---- 4 files changed, 235 insertions(+), 23 deletions(-) create mode 100644 src/assets/ai/copy.svg create mode 100644 src/assets/ai/delete.svg diff --git a/package.json b/package.json index b450bf69..db819026 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "private": false, "scripts": { "i": "pnpm install", - "dev": "vite", + "dev": "vite --mode env.local", "dev-server": "vite --mode dev", "ts:check": "vue-tsc --noEmit", "build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build", diff --git a/src/assets/ai/copy.svg b/src/assets/ai/copy.svg new file mode 100644 index 00000000..f51f8d81 --- /dev/null +++ b/src/assets/ai/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/delete.svg b/src/assets/ai/delete.svg new file mode 100644 index 00000000..d2ee18ed --- /dev/null +++ b/src/assets/ai/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index ec49b774..116f0e34 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -5,7 +5,7 @@
- + 新建对话 @@ -17,11 +17,11 @@ @keyup="searchConversation" > -
+
置顶 @@ -32,15 +32,15 @@ @click="changeConversation(conversation)" >
- + {{ conversation.title }}
- + - +
@@ -50,12 +50,12 @@
- + 角色仓库
- - 清空未置顶对话 + + 清空未置顶对话
@@ -67,25 +67,96 @@ 标题......
- 3.5-turbo-0125 - - + 3.5-turbo-0125 + - + - + + + +
- 对话列表 - - + + +
+ +
+
+ +
+
+
+ 2024-05-10 22:38 +
+
+ + 如果您想获取某个网页或程序的截图,可以使用浏览器自带的截图功能,或者使用第三方截图工具,如Snipping + Tool、FastStone Capture等。如果您想将屏幕上的某个区域截取下来,可以使用键盘上的“Prt + Sc”键(或“Print Screen”键)来获取整个屏幕的截图,并将其粘贴到图像编辑软件中进行编辑和保存。 + 如果您需要更具体的帮助,例如如何使用特定的截图工具或如何编辑截图,请提供更多详细信息,我将尽力为您提供帮助。 + + +
+
+
+ + 复制 +
+
+ + 删除 +
+
+
+
+ +
+
+ +
+
+
+ 2024-05-10 22:38 +
+
+ + 今天天气 + +
+
+
+ + 复制 +
+
+ + 删除 +
+
+
+ +
+
+ + +
+ + +
+ + 发送 +
@@ -172,8 +243,10 @@ const searchConversation = () => { border-radius: 5px; align-items: center; line-height: 30px; + &.active { background-color: #e6e6e6; + .button { display: inline-block; } @@ -184,6 +257,7 @@ const searchConversation = () => { flex-direction: row; align-items: center; } + .title { padding: 5px 10px; max-width: 220px; @@ -192,6 +266,7 @@ const searchConversation = () => { white-space: nowrap; text-overflow: ellipsis; } + .avatar { width: 28px; height: 28px; @@ -199,6 +274,7 @@ const searchConversation = () => { flex-direction: row; justify-items: center; } + // 对话编辑、删除 .button-wrapper { right: 2px; @@ -206,6 +282,7 @@ const searchConversation = () => { flex-direction: row; justify-items: center; color: #606266; + .el-icon { margin-right: 5px; } @@ -227,6 +304,7 @@ const searchConversation = () => { color: #606266; padding: 0; margin: 0; + > span { margin-left: 5px; } @@ -234,6 +312,7 @@ const searchConversation = () => { } } +// 头部 .detail-container { background: #ffffff; @@ -243,16 +322,147 @@ const searchConversation = () => { align-items: center; justify-content: space-between; background: #fbfbfb; + box-shadow: 0 0 0 0 #dcdfe6; .title { - font-size: 23px; + font-size: 18px; font-weight: bold; } } +} +// 中间 +.chat-list { + display: flex; + flex-direction: column; + + .message-item { + margin-top: 50px; + } + + .left-message { + display: flex; + flex-direction: row; + + } + + .right-message { + display: flex; + flex-direction: row-reverse; + justify-content: flex-start; + } + + .avatar { + //height: 170px; + //width: 170px; + } + + .message { + display: flex; + flex-direction: column; + text-align: left; + margin-left: 15px; + + .time { + text-align: left; + line-height: 30px; + } + + .left-text-container { + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background-color: #e4e4e4; + box-shadow: 0 0 0 1px #e4e4e4; + border-radius: 10px; + padding: 10px 10px 5px 10px; + + .left-text { + color: #393939; + } + } + + .right-text-container { + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background-color: #267fff; + color: #FFF; + box-shadow: 0 0 0 1px #267fff; + border-radius: 10px; + padding: 10px; + + .right-text { + color: #FFF; + } + } + + .left-btns, .right-btns { + display: flex; + flex-direction: row; + margin-top: 8px; + } + + + } + + // 复制、删除按钮 + .btn-cus { + display: flex; + background-color: transparent; + align-items: center; + + .btn-image { + height: 20px; + margin-right: 5px; + } + + .btn-cus-text { + color: #757575; + } + } + + .btn-cus:hover { + cursor: pointer; + } + + .btn-cus:focus { + background-color: #8c939d; + } +} + + +// 底部 +.footer-container { + display: flex; + flex-direction: column; + height: auto; + border: 1px solid #e3e3e3; + border-radius: 10px; + margin: 20px 20px; + padding: 9px 10px; .prompt-input { + height: 80px; + //box-shadow: none; + border: none; + box-sizing: border-box; + resize: none; + padding: 0px 2px; + //padding: 5px 5px; + overflow: hidden; + } + + .prompt-input:focus { + outline: none; + } + + .prompt-btns { + display: flex; + justify-content: space-between; + padding-bottom: 0px; + padding-top: 5px; } } From b19d56ad026f705e09505ed40413c18f47de893f Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 10 May 2024 23:38:44 +0800 Subject: [PATCH 009/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E8=B0=83=E6=95=B4=20right=20=E5=AF=B9=E8=AF=9D=20=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=E8=B7=9D=E7=A6=BB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 116f0e34..4b4bee22 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -361,7 +361,7 @@ const searchConversation = () => { display: flex; flex-direction: column; text-align: left; - margin-left: 15px; + margin: 0 15px; .time { text-align: left; From f01dc0a539d7e52229bd64b5ac5b9afef86a308a Mon Sep 17 00:00:00 2001 From: scholar <1145227973@qq.com> Date: Sat, 11 May 2024 11:26:47 +0800 Subject: [PATCH 010/235] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/crm/statistics/performance/index.vue | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/views/crm/statistics/performance/index.vue b/src/views/crm/statistics/performance/index.vue index ea43ed4d..469569af 100644 --- a/src/views/crm/statistics/performance/index.vue +++ b/src/views/crm/statistics/performance/index.vue @@ -75,6 +75,7 @@ import { defaultProps, handleTree } from '@/utils/tree' import ContractCountPerformance from './components/ContractCountPerformance.vue' import ContractPricePerformance from './components/ContractPricePerformance.vue' import ReceivablePricePerformance from './components/ReceivablePricePerformance.vue' +import dayjs from "dayjs" defineOptions({ name: 'CrmStatisticsCustomer' }) @@ -82,8 +83,8 @@ const queryParams = reactive({ deptId: useUserStore().getUser.deptId, userId: undefined, times: [ - // 默认显示当年的数据 - formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))) + formatDate(beginOfDay(new Date(new Date().getFullYear(),0, 1, 0, 0, 0))), // 默认查询当年的数据,比如2024年,起始时间2024-01-01 00:00:00 + formatDate(beginOfDay(new Date(new Date().getFullYear()+1,0, 1, 0, 0, 0))) //查询时间范围结束时间,2025-01-01 00:00:00 ] }) @@ -111,13 +112,13 @@ const handleQuery = () => { // 从 queryParams.times[0] 中获取到了年份 const selectYear = parseInt(queryParams.times[0]) - // 创建一个新的 Date 对象,设置为指定的年份的第一天 - const fullDate = new Date(selectYear, 0, 1, 0, 0, 0) + // 创建一个新的 Date 对象,设置为指定的年份的第一天,以及第二年的第一天,以时间段的方式,将查询时间传递给后端 + const fullDate = new Date(selectYear, 0, 1, 0, 0, 0) //比如2024年,起始时间2024-01-01 00:00:00 + const nextFullDate = new Date(selectYear+1, 0, 1, 0, 0, 0) //查询时间范围结束时间,2025-01-01 00:00:00 // 将完整的日期时间格式化为需要的字符串形式,比如 2004-01-01 00:00:00 - queryParams.times[0] = `${fullDate.getFullYear()}-${ - String(fullDate.getMonth() + 1).padStart(2, '0') - }-${String(fullDate.getDate()).padStart(2, '0')} ${String(fullDate.getHours()).padStart(2, '0')}:${String(fullDate.getMinutes()).padStart(2, '0')}:${String(fullDate.getSeconds()).padStart(2, '0')}` + queryParams.times[0] = dayjs(fullDate).format('YYYY-MM-DD HH:mm:ss') + queryParams.times[1] = dayjs(nextFullDate).format('YYYY-MM-DD HH:mm:ss') switch (activeTab.value) { case 'ContractCountPerformance': From dd2c4b6b3ca8a3992b11535d1a12f2fe875fb4e7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 11 May 2024 19:46:33 +0800 Subject: [PATCH 011/235] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9AAPI=20=E6=A8=A1=E5=9E=8B=E7=AE=A1=E7=90=86=EF=BC=88?= =?UTF-8?q?=E5=AE=8C=E5=96=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ai/model/apiKey/index.ts | 5 +++ src/views/ai/model/apiKey/ApiKeyForm.vue | 18 ++++---- src/views/ai/model/apiKey/index.vue | 9 ++-- .../ai/model/chatModel/ChatModelForm.vue | 41 +++++++++++++++---- src/views/ai/model/chatModel/index.vue | 37 ++++++++++------- 5 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts index c96f2a66..60d8a46c 100644 --- a/src/api/ai/model/apiKey/index.ts +++ b/src/api/ai/model/apiKey/index.ts @@ -17,6 +17,11 @@ export const ApiKeyApi = { return await request.get({ url: `/ai/api-key/page`, params }) }, + // 获得 API 密钥列表 + getApiKeyList: async () => { + return await request.get({ url: `/ai/api-key/simple-list` }) + }, + // 查询 API 密钥详情 getApiKey: async (id: number) => { return await request.get({ url: `/ai/api-key/get?id=` + id }) diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue index 2f9ba580..a8fc0128 100644 --- a/src/views/ai/model/apiKey/ApiKeyForm.vue +++ b/src/views/ai/model/apiKey/ApiKeyForm.vue @@ -7,13 +7,7 @@ label-width="120px" v-loading="formLoading" > - - - - - - - + - - + + + + + + + + diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue index fcafc463..6daf6a7d 100644 --- a/src/views/ai/model/apiKey/index.vue +++ b/src/views/ai/model/apiKey/index.vue @@ -60,15 +60,14 @@ - - - - + - + + + From 73e851602420bac1dbf34103ecce0c1de3b5b1a6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 11 May 2024 22:30:44 +0800 Subject: [PATCH 012/235] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91CRM=EF=BC=9A=E4=BC=98=E5=8C=96=E5=91=98?= =?UTF-8?q?=E5=B7=A5=E4=B8=9A=E7=BB=A9=E7=BB=9F=E8=AE=A1=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ContractCountPerformance.vue | 102 +++++++++++------- .../components/ContractPricePerformance.vue | 102 +++++++++++------- .../components/ReceivablePricePerformance.vue | 102 +++++++++++------- .../crm/statistics/performance/index.vue | 34 +++--- 4 files changed, 197 insertions(+), 143 deletions(-) diff --git a/src/views/crm/statistics/performance/components/ContractCountPerformance.vue b/src/views/crm/statistics/performance/components/ContractCountPerformance.vue index 5dc4d9e7..fa5a897b 100644 --- a/src/views/crm/statistics/performance/components/ContractCountPerformance.vue +++ b/src/views/crm/statistics/performance/components/ContractCountPerformance.vue @@ -10,9 +10,15 @@ - + @@ -23,7 +29,7 @@ import { EChartsOption } from 'echarts' import { StatisticsPerformanceApi, StatisticsPerformanceRespVO -} from "@/api/crm/statistics/performance" +} from '@/api/crm/statistics/performance' defineOptions({ name: 'ContractCountPerformance' }) const props = defineProps<{ queryParams: any }>() // 搜索参数 @@ -86,29 +92,30 @@ const echartsOption = reactive({ type: 'shadow' } }, - yAxis: [{ - type: 'value', - name: '数量(个)', - axisTick: { - show: false - }, - axisLabel: { - color: '#BDBDBD', - formatter: '{value}' - }, - /** 坐标轴轴线相关设置 */ - axisLine: { - lineStyle: { - color: '#BDBDBD' + yAxis: [ + { + type: 'value', + name: '数量(个)', + axisTick: { + show: false + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}' + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD' + } + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6' + } } }, - splitLine: { - show: true, - lineStyle: { - color: '#e6e6e6' - } - } - }, { type: 'value', name: '', @@ -134,7 +141,8 @@ const echartsOption = reactive({ color: '#e6e6e6' } } - }], + } + ], xAxis: { type: 'category', name: '日期', @@ -152,9 +160,7 @@ const loadData = async () => { // 2.1 更新 Echarts 数据 if (echartsOption.xAxis && echartsOption.xAxis['data']) { - echartsOption.xAxis['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.time - ) + echartsOption.xAxis['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => s.time) } if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) { echartsOption.series[0]['data'] = performanceList.map( @@ -165,16 +171,20 @@ const loadData = async () => { echartsOption.series[1]['data'] = performanceList.map( (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) - echartsOption.series[3]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? ((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount*100).toFixed(2) : 'NULL' + echartsOption.series[3]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => + s.lastMonthCount !== 0 + ? (((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount) * 100).toFixed(2) + : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { echartsOption.series[2]['data'] = performanceList.map( (s: StatisticsPerformanceRespVO) => s.lastYearCount ) - echartsOption.series[4]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? ((s.currentMonthCount - s.lastYearCount) / s.lastYearCount*100).toFixed(2) : 'NULL' + echartsOption.series[4]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => + s.lastYearCount !== 0 + ? (((s.currentMonthCount - s.lastYearCount) / s.lastYearCount) * 100).toFixed(2) + : 'NULL' ) } @@ -182,28 +192,38 @@ const loadData = async () => { list.value = performanceList convertListData() loading.value = false - } // 初始化数据 -const columnsData = reactive([]); -const tableData = reactive([{title: '当月合同数量统计(个)'}, {title: '上月合同数量统计(个)'}, - {title: '去年当月合同数量统计(个)'}, {title: '环比增长率(%)'}, {title: '同比增长率(%)'}]) +const columnsData = reactive([]) +const tableData = reactive([ + { title: '当月合同数量统计(个)' }, + { title: '上月合同数量统计(个)' }, + { title: '去年当月合同数量统计(个)' }, + { title: '环比增长率(%)' }, + { title: '同比增长率(%)' } +]) // 定义 convertListData 方法,数据行列转置,展示每月数据 const convertListData = () => { - const columnObj = {label: '日期', prop: 'title'} - columnsData.splice(0, columnsData.length);//清空数组 + const columnObj = { label: '日期', prop: 'title' } + columnsData.splice(0, columnsData.length) //清空数组 columnsData.push(columnObj) list.value.forEach((item, index) => { - const columnObj = {label: item.time, prop: 'prop' + index} + const columnObj = { label: item.time, prop: 'prop' + index } columnsData.push(columnObj) tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? ((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount * 100).toFixed(2) : 'NULL' - tableData[4]['prop' + index] = item.lastYearCount !== 0 ? ((item.currentMonthCount - item.lastYearCount) / item.lastYearCount * 100).toFixed(2) : 'NULL' + tableData[3]['prop' + index] = + item.lastMonthCount !== 0 + ? (((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount) * 100).toFixed(2) + : 'NULL' + tableData[4]['prop' + index] = + item.lastYearCount !== 0 + ? (((item.currentMonthCount - item.lastYearCount) / item.lastYearCount) * 100).toFixed(2) + : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/components/ContractPricePerformance.vue b/src/views/crm/statistics/performance/components/ContractPricePerformance.vue index 0c33dc22..dd52d9fb 100644 --- a/src/views/crm/statistics/performance/components/ContractPricePerformance.vue +++ b/src/views/crm/statistics/performance/components/ContractPricePerformance.vue @@ -10,9 +10,15 @@ - + @@ -23,7 +29,7 @@ import { EChartsOption } from 'echarts' import { StatisticsPerformanceApi, StatisticsPerformanceRespVO -} from "@/api/crm/statistics/performance" +} from '@/api/crm/statistics/performance' defineOptions({ name: 'ContractPricePerformance' }) const props = defineProps<{ queryParams: any }>() // 搜索参数 @@ -86,29 +92,30 @@ const echartsOption = reactive({ type: 'shadow' } }, - yAxis: [{ - type: 'value', - name: '金额(元)', - axisTick: { - show: false - }, - axisLabel: { - color: '#BDBDBD', - formatter: '{value}' - }, - /** 坐标轴轴线相关设置 */ - axisLine: { - lineStyle: { - color: '#BDBDBD' + yAxis: [ + { + type: 'value', + name: '金额(元)', + axisTick: { + show: false + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}' + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD' + } + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6' + } } }, - splitLine: { - show: true, - lineStyle: { - color: '#e6e6e6' - } - } - }, { type: 'value', name: '', @@ -134,7 +141,8 @@ const echartsOption = reactive({ color: '#e6e6e6' } } - }], + } + ], xAxis: { type: 'category', name: '日期', @@ -152,9 +160,7 @@ const loadData = async () => { // 2.1 更新 Echarts 数据 if (echartsOption.xAxis && echartsOption.xAxis['data']) { - echartsOption.xAxis['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.time - ) + echartsOption.xAxis['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => s.time) } if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) { echartsOption.series[0]['data'] = performanceList.map( @@ -165,16 +171,20 @@ const loadData = async () => { echartsOption.series[1]['data'] = performanceList.map( (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) - echartsOption.series[3]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? ((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount*100).toFixed(2) : 'NULL' + echartsOption.series[3]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => + s.lastMonthCount !== 0 + ? (((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount) * 100).toFixed(2) + : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { echartsOption.series[2]['data'] = performanceList.map( (s: StatisticsPerformanceRespVO) => s.lastYearCount ) - echartsOption.series[4]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? ((s.currentMonthCount - s.lastYearCount) / s.lastYearCount*100).toFixed(2) : 'NULL' + echartsOption.series[4]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => + s.lastYearCount !== 0 + ? (((s.currentMonthCount - s.lastYearCount) / s.lastYearCount) * 100).toFixed(2) + : 'NULL' ) } @@ -182,28 +192,38 @@ const loadData = async () => { list.value = performanceList convertListData() loading.value = false - } // 初始化数据 -const columnsData = reactive([]); -const tableData = reactive([{title: '当月合同金额统计(元)'}, {title: '上月合同金额统计(元)'}, {title: '去年当月合同金额统计(元)'}, - {title: '环比增长率(%)'}, {title: '同比增长率(%)'}]) +const columnsData = reactive([]) +const tableData = reactive([ + { title: '当月合同金额统计(元)' }, + { title: '上月合同金额统计(元)' }, + { title: '去年当月合同金额统计(元)' }, + { title: '环比增长率(%)' }, + { title: '同比增长率(%)' } +]) // 定义 init 方法 const convertListData = () => { - const columnObj = {label: '日期', prop: 'title'} - columnsData.splice(0, columnsData.length)//清空数组 + const columnObj = { label: '日期', prop: 'title' } + columnsData.splice(0, columnsData.length) //清空数组 columnsData.push(columnObj) list.value.forEach((item, index) => { - const columnObj = {label: item.time, prop: 'prop' + index} + const columnObj = { label: item.time, prop: 'prop' + index } columnsData.push(columnObj) tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? ((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount*100).toFixed(2) : 'NULL' - tableData[4]['prop' + index] = item.lastYearCount !== 0 ? ((item.currentMonthCount - item.lastYearCount) / item.lastYearCount*100).toFixed(2) : 'NULL' + tableData[3]['prop' + index] = + item.lastMonthCount !== 0 + ? (((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount) * 100).toFixed(2) + : 'NULL' + tableData[4]['prop' + index] = + item.lastYearCount !== 0 + ? (((item.currentMonthCount - item.lastYearCount) / item.lastYearCount) * 100).toFixed(2) + : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue b/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue index aa3f5b19..169f074b 100644 --- a/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue +++ b/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue @@ -10,9 +10,15 @@ - + @@ -23,7 +29,7 @@ import { EChartsOption } from 'echarts' import { StatisticsPerformanceApi, StatisticsPerformanceRespVO -} from "@/api/crm/statistics/performance" +} from '@/api/crm/statistics/performance' defineOptions({ name: 'ContractPricePerformance' }) const props = defineProps<{ queryParams: any }>() // 搜索参数 @@ -86,29 +92,30 @@ const echartsOption = reactive({ type: 'shadow' } }, - yAxis: [{ - type: 'value', - name: '金额(元)', - axisTick: { - show: false - }, - axisLabel: { - color: '#BDBDBD', - formatter: '{value}' - }, - /** 坐标轴轴线相关设置 */ - axisLine: { - lineStyle: { - color: '#BDBDBD' + yAxis: [ + { + type: 'value', + name: '金额(元)', + axisTick: { + show: false + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}' + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD' + } + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6' + } } }, - splitLine: { - show: true, - lineStyle: { - color: '#e6e6e6' - } - } - }, { type: 'value', name: '', @@ -134,7 +141,8 @@ const echartsOption = reactive({ color: '#e6e6e6' } } - }], + } + ], xAxis: { type: 'category', name: '日期', @@ -152,9 +160,7 @@ const loadData = async () => { // 2.1 更新 Echarts 数据 if (echartsOption.xAxis && echartsOption.xAxis['data']) { - echartsOption.xAxis['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.time - ) + echartsOption.xAxis['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => s.time) } if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) { echartsOption.series[0]['data'] = performanceList.map( @@ -165,16 +171,20 @@ const loadData = async () => { echartsOption.series[1]['data'] = performanceList.map( (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) - echartsOption.series[3]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastMonthCount !== 0 ? ((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount*100).toFixed(2) : 'NULL' + echartsOption.series[3]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => + s.lastMonthCount !== 0 + ? (((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount) * 100).toFixed(2) + : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[1]['data']) { echartsOption.series[2]['data'] = performanceList.map( (s: StatisticsPerformanceRespVO) => s.lastYearCount ) - echartsOption.series[4]['data'] = performanceList.map( - (s: StatisticsPerformanceRespVO) => s.lastYearCount !== 0 ? ((s.currentMonthCount - s.lastYearCount) / s.lastYearCount*100).toFixed(2) : 'NULL' + echartsOption.series[4]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => + s.lastYearCount !== 0 + ? (((s.currentMonthCount - s.lastYearCount) / s.lastYearCount) * 100).toFixed(2) + : 'NULL' ) } @@ -182,28 +192,38 @@ const loadData = async () => { list.value = performanceList convertListData() loading.value = false - } // 初始化数据 -const columnsData = reactive([]); -const tableData = reactive([{title: '当月回款金额统计(元)'}, {title: '上月回款金额统计(元)'}, - {title: '去年当月回款金额统计(元)'}, {title: '环比增长率(%)'}, {title: '同比增长率(%)'}]); +const columnsData = reactive([]) +const tableData = reactive([ + { title: '当月回款金额统计(元)' }, + { title: '上月回款金额统计(元)' }, + { title: '去年当月回款金额统计(元)' }, + { title: '环比增长率(%)' }, + { title: '同比增长率(%)' } +]) // 定义 init 方法 const convertListData = () => { - const columnObj = {label: '日期', prop: 'title'} - columnsData.splice(0, columnsData.length)//清空数组 + const columnObj = { label: '日期', prop: 'title' } + columnsData.splice(0, columnsData.length) //清空数组 columnsData.push(columnObj) list.value.forEach((item, index) => { - const columnObj = {label: item.time, prop: 'prop' + index} + const columnObj = { label: item.time, prop: 'prop' + index } columnsData.push(columnObj) tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - tableData[3]['prop' + index] = item.lastMonthCount !== 0 ? ((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount*100).toFixed(2) : 'NULL' - tableData[4]['prop' + index] = item.lastYearCount !== 0 ? ((item.currentMonthCount - item.lastYearCount) / item.lastYearCount*100).toFixed(2) : 'NULL' + tableData[3]['prop' + index] = + item.lastMonthCount !== 0 + ? (((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount) * 100).toFixed(2) + : 'NULL' + tableData[4]['prop' + index] = + item.lastYearCount !== 0 + ? (((item.currentMonthCount - item.lastYearCount) / item.lastYearCount) * 100).toFixed(2) + : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/index.vue b/src/views/crm/statistics/performance/index.vue index 469569af..822afec9 100644 --- a/src/views/crm/statistics/performance/index.vue +++ b/src/views/crm/statistics/performance/index.vue @@ -60,7 +60,10 @@ - + @@ -70,12 +73,11 @@ import * as DeptApi from '@/api/system/dept' import * as UserApi from '@/api/system/user' import { useUserStore } from '@/store/modules/user' -import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime' +import { beginOfDay, endOfDay, formatDate } from '@/utils/formatTime' import { defaultProps, handleTree } from '@/utils/tree' import ContractCountPerformance from './components/ContractCountPerformance.vue' import ContractPricePerformance from './components/ContractPricePerformance.vue' import ReceivablePricePerformance from './components/ReceivablePricePerformance.vue' -import dayjs from "dayjs" defineOptions({ name: 'CrmStatisticsCustomer' }) @@ -83,8 +85,8 @@ const queryParams = reactive({ deptId: useUserStore().getUser.deptId, userId: undefined, times: [ - formatDate(beginOfDay(new Date(new Date().getFullYear(),0, 1, 0, 0, 0))), // 默认查询当年的数据,比如2024年,起始时间2024-01-01 00:00:00 - formatDate(beginOfDay(new Date(new Date().getFullYear()+1,0, 1, 0, 0, 0))) //查询时间范围结束时间,2025-01-01 00:00:00 + formatDate(beginOfDay(new Date(new Date().getFullYear(), 0, 1))), + formatDate(endOfDay(new Date(new Date().getFullYear(), 11, 31))) ] }) @@ -100,26 +102,18 @@ const userListByDeptId = computed(() => // 活跃标签 const activeTab = ref('ContractCountPerformance') -// 1.员工合同数量统计 -const ContractCountPerformanceRef = ref() -// 2.员工合同金额统计 -const ContractPricePerformanceRef = ref() -// 3.员工回款金额统计 -const ReceivablePricePerformanceRef = ref() +const ContractCountPerformanceRef = ref() // 员工合同数量统计 +const ContractPricePerformanceRef = ref() // 员工合同金额统计 +const ReceivablePricePerformanceRef = ref() // 员工回款金额统计 /** 搜索按钮操作 */ -const handleQuery = () => { +const handleQuery = () => { // 从 queryParams.times[0] 中获取到了年份 const selectYear = parseInt(queryParams.times[0]) + queryParams.times[0] = formatDate(beginOfDay(new Date(selectYear, 0, 1))) + queryParams.times[1] = formatDate(endOfDay(new Date(selectYear, 11, 31))) - // 创建一个新的 Date 对象,设置为指定的年份的第一天,以及第二年的第一天,以时间段的方式,将查询时间传递给后端 - const fullDate = new Date(selectYear, 0, 1, 0, 0, 0) //比如2024年,起始时间2024-01-01 00:00:00 - const nextFullDate = new Date(selectYear+1, 0, 1, 0, 0, 0) //查询时间范围结束时间,2025-01-01 00:00:00 - - // 将完整的日期时间格式化为需要的字符串形式,比如 2004-01-01 00:00:00 - queryParams.times[0] = dayjs(fullDate).format('YYYY-MM-DD HH:mm:ss') - queryParams.times[1] = dayjs(nextFullDate).format('YYYY-MM-DD HH:mm:ss') - + // 执行查询 switch (activeTab.value) { case 'ContractCountPerformance': ContractCountPerformanceRef.value?.loadData?.() From b116d82376119753924a39332ddca3c9a4f4acb7 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 12 May 2024 14:28:27 +0800 Subject: [PATCH 013/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20chat=20=E8=81=8A=E5=A4=A9=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E3=80=81=E5=88=A0=E9=99=A4=E3=80=81=E5=A4=8D=E5=88=B6?= =?UTF-8?q?=E5=88=B0=E7=B2=98=E8=B4=B4=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ai/chat/message/index.ts | 41 +++ src/views/ai/chat/components/MessageList.vue | 276 +++++++++++++++++++ src/views/ai/chat/index.vue | 180 +----------- 3 files changed, 332 insertions(+), 165 deletions(-) create mode 100644 src/api/ai/chat/message/index.ts create mode 100644 src/views/ai/chat/components/MessageList.vue diff --git a/src/api/ai/chat/message/index.ts b/src/api/ai/chat/message/index.ts new file mode 100644 index 00000000..3dc1ea0f --- /dev/null +++ b/src/api/ai/chat/message/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +// 聊天VO +export interface ChatMessageVO { + id: number // 编号 + conversationId: string // 会话编号 + type: string // 消息类型 + userId: string // 用户编号 + roleId: string // 角色编号 + model: number // 模型标志 + modelId: number // 模型编号 + content: number // 聊天内容 + tokens: number // 消耗 Token 数量 + createTime: Date // 创建时间 +} + +export interface ChatMessageSendVO { + conversationId: string // 会话编号 + content: number // 聊天内容 +} + +// AI chat 聊天 +export const ChatMessageApi = { + + // 消息列表 + messageList: async (conversationId: string) => { + return await request.get({ url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}`}) + }, + + // 发送 send 消息 + send: async (data: ChatMessageSendVO) => { + return await request.post({ url: `/ai/chat/message/send`, data }) + }, + + + // 发送 send 消息 + delete: async (id: string) => { + return await request.delete({ url: `/ai/chat/message/delete?id=${id}` }) + }, + +} diff --git a/src/views/ai/chat/components/MessageList.vue b/src/views/ai/chat/components/MessageList.vue new file mode 100644 index 00000000..3038f886 --- /dev/null +++ b/src/views/ai/chat/components/MessageList.vue @@ -0,0 +1,276 @@ + + + + + + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 4b4bee22..f3dc471f 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -81,73 +81,10 @@
- - -
- -
-
- -
-
-
- 2024-05-10 22:38 -
-
- - 如果您想获取某个网页或程序的截图,可以使用浏览器自带的截图功能,或者使用第三方截图工具,如Snipping - Tool、FastStone Capture等。如果您想将屏幕上的某个区域截取下来,可以使用键盘上的“Prt - Sc”键(或“Print Screen”键)来获取整个屏幕的截图,并将其粘贴到图像编辑软件中进行编辑和保存。 - 如果您需要更具体的帮助,例如如何使用特定的截图工具或如何编辑截图,请提供更多详细信息,我将尽力为您提供帮助。 - - -
-
-
- - 复制 -
-
- - 删除 -
-
-
-
- -
-
- -
-
-
- 2024-05-10 22:38 -
-
- - 今天天气 - -
-
-
- - 复制 -
-
- - 删除 -
-
-
- -
-
- + + + +
- 发送 + 发送
@@ -100,25 +158,15 @@ diff --git a/src/components/MdPreview/md.ts b/src/components/MdPreview/md.ts new file mode 100644 index 00000000..81aa7b65 --- /dev/null +++ b/src/components/MdPreview/md.ts @@ -0,0 +1,30 @@ + +// @ts-ignore +import markdownit from 'markdown-it'; +import hljs from 'highlight.js'; // https://highlightjs.org +import katexPlugin from '@iktakahiro/markdown-it-katex'; +const codeTool = (text: string) => ``; + +const md = markdownit({ + html: true, + linkfy: true, + highlight: function (str: string, lang: string) { + const baseText = str + if (lang && hljs.getLanguage(lang)) { + try { + return '
' +
+                    hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
+                    '' + codeTool(baseText) + '
'; + } catch (__) { } + } + return '
' + md.utils.escapeHtml(str) + '' + codeTool(baseText) + '
'; + } +}); + +md.use(katexPlugin); + +export default md; diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 2e5cead6..317f6838 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -98,11 +98,12 @@ {{formatDate(item.createTime)}}
-
+ +
- + 复制
@@ -124,7 +125,7 @@ {{formatDate(item.createTime)}}
-
+
{{item.content}}
@@ -161,21 +162,30 @@ diff --git a/src/views/ai/model/chatRole/ChatRoleForm.vue b/src/views/ai/model/chatRole/ChatRoleForm.vue index 1e184bff..b83dea51 100644 --- a/src/views/ai/model/chatRole/ChatRoleForm.vue +++ b/src/views/ai/model/chatRole/ChatRoleForm.vue @@ -7,29 +7,37 @@ label-width="100px" v-loading="formLoading" > - - - - + + + + + + - - - - + - + - + @@ -42,7 +50,10 @@ - + + + + import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole' +import { CommonStatusEnum } from '@/utils/constants' +import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel' /** AI 聊天角色 表单 */ defineOptions({ name: 'ChatRoleForm' }) @@ -84,8 +97,8 @@ const formData = ref({ description: undefined, welcomeMessage: undefined, systemMessage: undefined, - publicStatus: undefined, - status: undefined + publicStatus: true, + status: CommonStatusEnum.ENABLE }) const formRules = reactive({ name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }], @@ -98,6 +111,7 @@ const formRules = reactive({ publicStatus: [{ required: true, message: '是否公开不能为空', trigger: 'blur' }] }) const formRef = ref() // 表单 Ref +const chatModelList = ref([] as ChatModelVO[]) // 聊天模型列表 /** 打开弹窗 */ const open = async (type: string, id?: number) => { @@ -114,6 +128,8 @@ const open = async (type: string, id?: number) => { formLoading.value = false } } + // 获得下拉数据 + chatModelList.value = await ChatModelApi.getChatModelSimpleList(CommonStatusEnum.ENABLE) } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 @@ -153,8 +169,8 @@ const resetForm = () => { description: undefined, welcomeMessage: undefined, systemMessage: undefined, - publicStatus: undefined, - status: undefined + publicStatus: true, + status: CommonStatusEnum.ENABLE } formRef.value?.resetFields() } diff --git a/src/views/ai/model/chatRole/index.vue b/src/views/ai/model/chatRole/index.vue index 40759e87..552d9a68 100644 --- a/src/views/ai/model/chatRole/index.vue +++ b/src/views/ai/model/chatRole/index.vue @@ -59,13 +59,14 @@ - - - - + + + + - @@ -79,13 +80,7 @@ - + + + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 0da53f1e..735e5c90 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -1,81 +1,19 @@ + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index a2dd8c05..3ba1046c 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -34,7 +34,7 @@
- +
@@ -82,12 +82,10 @@ + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 704a693e..2d61132a 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -31,12 +31,15 @@
- +
- + +
+ +
- +
+ 上下文 +
() // 对话进行中 abort 控制 const inputTimeout = ref() // 处理输入中回车的定时器 const prompt = ref() // prompt const userInfo = ref() // 用户信息 +const enableContext = ref(true) // 是否开启上下文 const fullText = ref(''); const displayedText = ref(''); @@ -327,6 +330,7 @@ const doSendStream = async (userMessage: ChatMessageVO) => { userMessage.conversationId, // TODO 芋艿:这里可能要在优化; userMessage.content, conversationInAbortController.value, + enableContext.value, async (message) => { const data = JSON.parse(message.data) // TODO 芋艿:类型处理; // 如果内容为空,就不处理。 From e7403ba2a450d50100eecc086cf98264f9614711 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Wed, 22 May 2024 17:07:54 +0800 Subject: [PATCH 137/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI?= =?UTF-8?q?=20chat=20=E5=A2=9E=E5=8A=A0=20new=20chat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/MessageNewChat.vue | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/views/ai/chat/MessageNewChat.vue diff --git a/src/views/ai/chat/MessageNewChat.vue b/src/views/ai/chat/MessageNewChat.vue new file mode 100644 index 00000000..aac5f905 --- /dev/null +++ b/src/views/ai/chat/MessageNewChat.vue @@ -0,0 +1,50 @@ + + + + + From 89deddb9e06aa79af9e9fe4cde87644f57eb0346 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Wed, 22 May 2024 17:08:13 +0800 Subject: [PATCH 138/235] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo?= =?UTF-8?q?=E3=80=91AI=20=E5=88=A0=E9=99=A4=E5=AF=B9=E8=AF=9D=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E9=BB=98=E8=AE=A4=E9=80=89=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/Conversation.vue | 17 ++++++++++------- src/views/ai/chat/index.vue | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/views/ai/chat/Conversation.vue b/src/views/ai/chat/Conversation.vue index 46988a63..f735252c 100644 --- a/src/views/ai/chat/Conversation.vue +++ b/src/views/ai/chat/Conversation.vue @@ -174,15 +174,15 @@ const getChatConversationList = async () => { conversationList.value = res // 3、默认选中 if (!activeId?.value) { - await handleConversationClick(res[0].id) + // await handleConversationClick(res[0].id) } else { // tip: 删除的刚好是选中的,那么需要重新挑选一个来进行选中 - const filterConversationList = conversationList.value.filter(item => { - return item.id === activeId.value - }) - if (filterConversationList.length <= 0) { - await handleConversationClick(res[0].id) - } + // const filterConversationList = conversationList.value.filter(item => { + // return item.id === activeId.value + // }) + // if (filterConversationList.length <= 0) { + // await handleConversationClick(res[0].id) + // } } // 4、没有 任何对话情况 if (conversationList.value.length === 0) { @@ -367,6 +367,9 @@ watch(activeId, async (newValue, oldValue) => { activeConversationId.value = newValue as string }) +// 定义 public 方法 +defineExpose({createConversation}) + onMounted(async () => { // 默认选中 if (props.activeId != null) { diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 999e1736..0aaa164c 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -2,6 +2,7 @@
- {{ activeConversation?.title }} + {{ activeConversation?.title ? activeConversation?.title : '对话' }}
-
+
@@ -32,11 +33,12 @@
+ + -
@@ -93,6 +95,7 @@ import Conversation from './Conversation.vue' import Message from './Message.vue' import ChatEmpty from './ChatEmpty.vue' import MessageLoading from './MessageLoading.vue' +import MessageNewChat from './MessageNewChat.vue' import {ChatMessageApi, ChatMessageVO} from '@/api/ai/chat/message' import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' import { getUserProfile, ProfileVO } from '@/api/system/user/profile' @@ -126,6 +129,7 @@ const listLoadingTime = ref() // time定时器,如果加载速度很快 // 判断 消息列表 滚动的位置(用于判断是否需要滚动到消息最下方) const messageRef = ref() +const conversationRef = ref() const isComposing = ref(false) // 判断用户是否在输入 // 默认 role 头像 @@ -490,6 +494,14 @@ const getConversation = async (id: string | null) => { } } +/** + * 对话 - 新建 + */ +const handlerNewChat = async () => { + // 创建对话 + await conversationRef.value.createConversation() +} + // ============ message =========== /** From f1beb488ce06bcce4895a301ef473870255e42ad Mon Sep 17 00:00:00 2001 From: cherishsince Date: Wed, 22 May 2024 17:17:43 +0800 Subject: [PATCH 139/235] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo?= =?UTF-8?q?=E3=80=91AI=20chat=20=E6=A0=87=E9=A2=98=E5=90=8E=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=AF=B9=E8=AF=9D=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/index.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 0aaa164c..a1366272 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -13,6 +13,7 @@
{{ activeConversation?.title ? activeConversation?.title : '对话' }} + ({{list.length}})
From 03c6a6ce59361cfcf6850eedc37c5845487077c1 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 24 May 2024 10:32:08 +0800 Subject: [PATCH 140/235] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo?= =?UTF-8?q?=E3=80=91Chat=20message=20=E5=A2=9E=E5=8A=A0=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E5=92=8C=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/Message.vue | 24 ++++++++++++++++++++++-- src/views/ai/chat/index.vue | 18 +++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/views/ai/chat/Message.vue b/src/views/ai/chat/Message.vue index 1a33b4bd..20277562 100644 --- a/src/views/ai/chat/Message.vue +++ b/src/views/ai/chat/Message.vue @@ -43,6 +43,12 @@ + + + + + +
@@ -59,7 +65,7 @@ import MarkdownView from "@/components/MarkdownView/index.vue"; import {ChatMessageApi, ChatMessageVO} from "@/api/ai/chat/message"; import {useClipboard} from "@vueuse/core"; import {PropType} from "vue"; -import {ArrowDownBold} from "@element-plus/icons-vue"; +import {ArrowDownBold, Edit, RefreshRight} from "@element-plus/icons-vue"; const {copy} = useClipboard() // 初始化 copy 到粘贴板 // 判断 消息列表 滚动的位置(用于判断是否需要滚动到消息最下方) @@ -125,6 +131,20 @@ const onDelete = async (id) => { emits('onDeleteSuccess') } +/** + * 刷新 + */ +const onRefresh = async (message: ChatMessageVO) => { + emits('onRefresh', message) +} + +/** + * 编辑 + */ +const onEdit = async (message: ChatMessageVO) => { + emits('onEdit', message) +} + /** * 回到底部 */ @@ -151,7 +171,7 @@ watch(list, async (newValue, oldValue) => { defineExpose({scrollToBottom, handlerGoTop}) // 定义 emits -const emits = defineEmits(['onDeleteSuccess']) +const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // onMounted onMounted(async () => { diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index a1366272..ac97e1ec 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -39,7 +39,9 @@ + @on-delete-success="handlerMessageDelete" + @on-edit="handlerMessageEdit" + @on-refresh="handlerMessageRefresh"/>
@@ -513,6 +515,20 @@ const handlerMessageDelete = async () => { await getMessageList() } +/** + * 编辑 message + */ +const handlerMessageEdit = async (message: ChatMessageVO) => { + prompt.value = message.content +} + +/** + * 编辑 message + */ +const handlerMessageRefresh = async (message: ChatMessageVO) => { + await doSend(message.content) +} + /** * 回到顶部 */ From bebf1e7ae3b5372de72cc82aded87c183e107f86 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 24 May 2024 10:35:11 +0800 Subject: [PATCH 141/235] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo?= =?UTF-8?q?=E3=80=91Chat=20=E8=81=8A=E5=A4=A9=EF=BC=8C=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=B8=85=E7=A9=BA=E8=BE=93=E5=85=A5=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/Conversation.vue | 9 ++++++++- src/views/ai/chat/index.vue | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/views/ai/chat/Conversation.vue b/src/views/ai/chat/Conversation.vue index f735252c..946735f7 100644 --- a/src/views/ai/chat/Conversation.vue +++ b/src/views/ai/chat/Conversation.vue @@ -122,7 +122,12 @@ const props = defineProps({ }) // 定义钩子 -const emits = defineEmits(['onConversationClick', 'onConversationClear', 'onConversationDelete']) +const emits = defineEmits([ + 'onConversationCreate', + 'onConversationClick', + 'onConversationClear', + 'onConversationDelete' +]) /** * 对话 - 搜索 @@ -256,6 +261,8 @@ const createConversation = async () => { await getChatConversationList() // 3、选中对话 await handleConversationClick(conversationId) + // 4、回调 + emits('onConversationCreate') } /** diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index ac97e1ec..d9a44df1 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -3,6 +3,7 @@ { await getConversation(activeConversationId.value) } +/** + * 对话 - 创建 + */ +const handleConversationCreate = async () => { + // 创建新的对话,清空输入框 + prompt.value = '' +} + /** * 对话 - 点击 */ From 8fb54dbb7c2d797ae754cd97e6ceafdddbd1e8e5 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 24 May 2024 10:39:12 +0800 Subject: [PATCH 142/235] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo?= =?UTF-8?q?=E3=80=91Chat=20=E5=AF=B9=E8=AF=9D=E9=BB=98=E8=AE=A4=E9=80=89?= =?UTF-8?q?=E4=B8=AD=E7=AC=AC=E4=B8=80=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/Conversation.vue | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/views/ai/chat/Conversation.vue b/src/views/ai/chat/Conversation.vue index 946735f7..4b2d60ef 100644 --- a/src/views/ai/chat/Conversation.vue +++ b/src/views/ai/chat/Conversation.vue @@ -378,12 +378,17 @@ watch(activeId, async (newValue, oldValue) => { defineExpose({createConversation}) onMounted(async () => { + // 获取 对话列表 + await getChatConversationList() // 默认选中 if (props.activeId != null) { activeConversationId.value = props.activeId + } else { + // 首次默认选中第一个 + if (conversationList.value.length) { + activeConversationId.value = conversationList.value[0].id + } } - // 获取 对话列表 - await getChatConversationList() }) From 8764c6c4712bd51c0df22e0bbb32acb1fd98bfa0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 24 May 2024 15:39:05 +0800 Subject: [PATCH 143/235] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9A=E6=96=B0=E5=BB=BA=E5=AF=B9=E8=AF=9D=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E4=BD=BF=E7=94=A8=E9=BB=98=E8=AE=A4=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ai/chat/conversation/index.ts | 2 +- src/views/ai/chat/Message.vue | 59 ++++++++++++++++++--------- src/views/ai/chat/index.vue | 26 +++++++++--- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts index b363ea7b..0834643d 100644 --- a/src/api/ai/chat/conversation/index.ts +++ b/src/api/ai/chat/conversation/index.ts @@ -12,8 +12,8 @@ export interface ChatConversationVO { temperature: number // 温度参数 maxTokens: number // 单条回复的最大 Token 数量 maxContexts: number // 上下文的最大 Message 数量 - updateTime: number // 更新时间 // 额外字段 + systemMessage?: string // 角色设定 modelName?: string // 模型名字 roleAvatar?: string // 角色头像 modelMaxTokens?: string // 模型的单条回复的最大 Token 数量 diff --git a/src/views/ai/chat/Message.vue b/src/views/ai/chat/Message.vue index 20277562..445182e5 100644 --- a/src/views/ai/chat/Message.vue +++ b/src/views/ai/chat/Message.vue @@ -1,8 +1,7 @@ From 4c259cd60fb20c696b98da464e4cc4e12bcc5c4b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 May 2024 08:59:56 +0800 Subject: [PATCH 149/235] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9A=E5=AF=B9=E8=AF=9D=E7=AE=A1=E7=90=86=20100%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ai/chat/conversation/index.ts | 5 + src/api/ai/chat/message/index.ts | 17 +- .../ai/chat/manager/ChatConversationList.vue | 163 ++++++++++++++++ src/views/ai/chat/manager/ChatMessageList.vue | 175 ++++++++++++++++++ src/views/ai/chat/manager/index.vue | 173 +---------------- 5 files changed, 365 insertions(+), 168 deletions(-) create mode 100644 src/views/ai/chat/manager/ChatConversationList.vue create mode 100644 src/views/ai/chat/manager/ChatMessageList.vue diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts index 07643255..08ad26a1 100644 --- a/src/api/ai/chat/conversation/index.ts +++ b/src/api/ai/chat/conversation/index.ts @@ -55,5 +55,10 @@ export const ChatConversationApi = { // 获得对话分页 getChatConversationPage: async (params: any) => { return await request.get({ url: `/ai/chat/conversation/page`, params }) + }, + + // 管理员删除消息 + deleteChatConversationByAdmin: async (id: number) => { + return await request.delete({ url: `/ai/chat/conversation/delete-by-admin?id=${id}` }) } } diff --git a/src/api/ai/chat/message/index.ts b/src/api/ai/chat/message/index.ts index 7b46f9c2..f9e807a1 100644 --- a/src/api/ai/chat/message/index.ts +++ b/src/api/ai/chat/message/index.ts @@ -55,7 +55,7 @@ export const ChatMessageApi = { body: JSON.stringify({ conversationId, content, - useContext: enableContext, + useContext: enableContext }), onmessage: onMessage, onerror: onError, @@ -71,7 +71,18 @@ export const ChatMessageApi = { // 删除消息 - 对话所有消息 deleteByConversationId: async (conversationId: string) => { - return await request.delete({ url: `/ai/chat/message/delete-by-conversation-id?conversationId=${conversationId}` }) - } + return await request.delete({ + url: `/ai/chat/message/delete-by-conversation-id?conversationId=${conversationId}` + }) + }, + // 获得消息分页 + getChatMessagePage: async (params: any) => { + return await request.get({ url: '/ai/chat/message/page', params }) + }, + + // 管理员删除消息 + deleteChatMessageByAdmin: async (id: number) => { + return await request.delete({ url: `/ai/chat/message/delete-by-admin?id=${id}` }) + } } diff --git a/src/views/ai/chat/manager/ChatConversationList.vue b/src/views/ai/chat/manager/ChatConversationList.vue new file mode 100644 index 00000000..23933f01 --- /dev/null +++ b/src/views/ai/chat/manager/ChatConversationList.vue @@ -0,0 +1,163 @@ + + + diff --git a/src/views/ai/chat/manager/ChatMessageList.vue b/src/views/ai/chat/manager/ChatMessageList.vue new file mode 100644 index 00000000..0d841840 --- /dev/null +++ b/src/views/ai/chat/manager/ChatMessageList.vue @@ -0,0 +1,175 @@ + + + diff --git a/src/views/ai/chat/manager/index.vue b/src/views/ai/chat/manager/index.vue index b36fa413..720ac1ea 100644 --- a/src/views/ai/chat/manager/index.vue +++ b/src/views/ai/chat/manager/index.vue @@ -1,174 +1,17 @@ From 46eb89695d9b502c6de6bca917515099ca3ff9c6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 May 2024 10:54:04 +0800 Subject: [PATCH 150/235] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91AI=EF=BC=9Areview=20=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/ChatEmpty.vue | 5 -- src/views/ai/chat/Conversation.vue | 41 +++++----- src/views/ai/chat/Message.vue | 4 +- src/views/ai/chat/components/Header.vue | 2 - src/views/ai/chat/index.vue | 41 +++++++--- src/views/ai/chat/role/RoleList.vue | 2 +- src/views/ai/chat/role/index.vue | 101 ++++++++++++------------ 7 files changed, 105 insertions(+), 91 deletions(-) diff --git a/src/views/ai/chat/ChatEmpty.vue b/src/views/ai/chat/ChatEmpty.vue index 68a53be9..4268b3d7 100644 --- a/src/views/ai/chat/ChatEmpty.vue +++ b/src/views/ai/chat/ChatEmpty.vue @@ -1,4 +1,3 @@ - diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 83231b57..5386563a 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -10,27 +10,27 @@ /> -
{{ activeConversation?.title ? activeConversation?.title : '对话' }} ({{list.length}})
- - + + +
- +
@@ -87,7 +87,7 @@ - + From cdf0a1137d95771d361db871e44fd11ab1c84080 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 May 2024 11:49:23 +0800 Subject: [PATCH 151/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI?= =?UTF-8?q?=EF=BC=9A=E5=AF=B9=E8=AF=9D=E7=9A=84=20user=E3=80=81role=20?= =?UTF-8?q?=E5=A4=B4=E5=83=8F=E4=BB=8E=E5=89=8D=E7=AB=AF=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/Conversation.vue | 3 +- src/views/ai/chat/Message.vue | 27 +++--- src/views/ai/chat/index.vue | 131 ++++++++++++++--------------- 3 files changed, 75 insertions(+), 86 deletions(-) diff --git a/src/views/ai/chat/Conversation.vue b/src/views/ai/chat/Conversation.vue index c8290175..2afeb9ac 100644 --- a/src/views/ai/chat/Conversation.vue +++ b/src/views/ai/chat/Conversation.vue @@ -43,7 +43,7 @@ :class="conversation.id === activeConversationId ? 'conversation active' : 'conversation'" >
- + {{ conversation.title }}
@@ -99,6 +99,7 @@ import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversatio import {ref} from "vue"; import Role from "@/views/ai/chat/role/index.vue"; import {Bottom, Top} from "@element-plus/icons-vue"; +import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' const message = useMessage() // 消息弹窗 diff --git a/src/views/ai/chat/Message.vue b/src/views/ai/chat/Message.vue index bf03becc..942961ed 100644 --- a/src/views/ai/chat/Message.vue +++ b/src/views/ai/chat/Message.vue @@ -1,10 +1,10 @@ From 60de1b973779e955b5cd4ab1a09905d780e79548 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sat, 25 May 2024 21:06:58 +0800 Subject: [PATCH 158/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91Dal?= =?UTF-8?q?l3=20=E5=A2=9E=E5=8A=A0=E5=9B=BE=E7=89=87=E7=94=9F=E6=88=90=20(?= =?UTF-8?q?function=20=E5=BE=85=E5=AE=9E=E7=8E=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/dall3/index.vue | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/views/ai/image/dall3/index.vue b/src/views/ai/image/dall3/index.vue index 95cc5a31..d9ba09f8 100644 --- a/src/views/ai/image/dall3/index.vue +++ b/src/views/ai/image/dall3/index.vue @@ -65,7 +65,10 @@
- +
+ + 生成内容 +
From f156ba18772acdd14f818d5ad1d661cd5abe0014 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sat, 25 May 2024 21:12:36 +0800 Subject: [PATCH 159/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI?= =?UTF-8?q?=20Image=20=E5=A2=9E=E5=8A=A0model=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/index.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/ai/image/index.vue b/src/views/ai/image/index.vue index 874f9b61..2d594b82 100644 --- a/src/views/ai/image/index.vue +++ b/src/views/ai/image/index.vue @@ -3,10 +3,11 @@
- +
@@ -20,10 +21,11 @@ From 12e87cc0f26fa00354c2778aa89fee83806c3d66 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sat, 25 May 2024 21:18:43 +0800 Subject: [PATCH 160/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91Ai?= =?UTF-8?q?=20Image=20midjourney=20=E5=A2=9E=E5=8A=A0=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E8=AF=8D=E3=80=81=E6=A8=A1=E5=9E=8B=E3=80=81=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/midjourney/index.vue | 277 +++++++++++++++++++++++- 1 file changed, 275 insertions(+), 2 deletions(-) diff --git a/src/views/ai/image/midjourney/index.vue b/src/views/ai/image/midjourney/index.vue index 93704d65..bbd523f6 100644 --- a/src/views/ai/image/midjourney/index.vue +++ b/src/views/ai/image/midjourney/index.vue @@ -1,10 +1,283 @@ - + From 86f3ae23016cced342f1385e2ad50cc7f85fcb69 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sat, 25 May 2024 21:20:43 +0800 Subject: [PATCH 161/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI?= =?UTF-8?q?=20image=20dall3=20=E8=B0=83=E6=95=B4=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E5=90=8D=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/dall3/index.vue | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/views/ai/image/dall3/index.vue b/src/views/ai/image/dall3/index.vue index d9ba09f8..587cfbf8 100644 --- a/src/views/ai/image/dall3/index.vue +++ b/src/views/ai/image/dall3/index.vue @@ -46,6 +46,7 @@ fit="contain" @click="handlerModelClick(model)" /> +
{{model.name}}
@@ -198,13 +199,20 @@ const handlerGenerateImage = async () => { margin-top: 15px; .modal-item { - width: 90px; - height: 50px; + width: 110px; //outline: 1px solid blue; overflow: hidden; display: flex; + flex-direction: column; + align-items: center; border: 3px solid transparent; cursor: pointer; + + .model-font { + font-size: 14px; + color: #3e3e3e; + font-weight: bold; + } } .selectModel { From ffa691c4ee9c622fa0cbd43dfc8c6739db323ed1 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 20:08:43 +0800 Subject: [PATCH 162/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A8=A1=E5=9E=8B=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/dall3/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ai/image/dall3/index.vue b/src/views/ai/image/dall3/index.vue index 587cfbf8..f141ca1c 100644 --- a/src/views/ai/image/dall3/index.vue +++ b/src/views/ai/image/dall3/index.vue @@ -99,7 +99,7 @@ const models = ref([ }, { key: 'ziran', - name: '清晰', + name: '自然', image: 'https://h5.cxyhub.com/images/model_2.png', }, ]) // 模型 From bfa396b127036558fb05c585f4b486525ea1e720 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 20:22:36 +0800 Subject: [PATCH 163/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=20midjourney=20=E6=A8=A1=E5=9E=8B=E5=9B=BE?= =?UTF-8?q?=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/midjourney/index.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/ai/image/midjourney/index.vue b/src/views/ai/image/midjourney/index.vue index bbd523f6..114a1467 100644 --- a/src/views/ai/image/midjourney/index.vue +++ b/src/views/ai/image/midjourney/index.vue @@ -95,12 +95,12 @@ const models = ref([ { key: 'qinxi', name: 'MJ', - image: 'https://h5.cxyhub.com/images/mj.jpeg', + image: 'https://bigpt8.com/pc/_nuxt/mj.34a61377.png', }, { key: 'ziran', name: 'NIJI', - image: 'https://h5.cxyhub.com/images/niji.jpeg', + image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png', }, ]) // 模型 @@ -210,7 +210,7 @@ const handlerGenerateImage = async () => { display: flex; flex-direction: column; align-items: center; - width: 110px; + width: 150px; //outline: 1px solid blue; overflow: hidden; border: 3px solid transparent; From 131e071b2d4cb048c981480dcb0b340b25024645 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 20:47:29 +0800 Subject: [PATCH 164/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91Ai?= =?UTF-8?q?=20Image=20task=20=E4=BB=BB=E5=8A=A1=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/ImageTask.vue | 68 ++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/views/ai/image/ImageTask.vue diff --git a/src/views/ai/image/ImageTask.vue b/src/views/ai/image/ImageTask.vue new file mode 100644 index 00000000..f4c4b9b2 --- /dev/null +++ b/src/views/ai/image/ImageTask.vue @@ -0,0 +1,68 @@ + + + + + From 9c8d10b70c58735fcf5f82e08f8a2cadc956cd17 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 20:59:42 +0800 Subject: [PATCH 165/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI?= =?UTF-8?q?=20image=20=E5=A2=9E=E5=8A=A0=E5=9B=BE=E7=89=87=E8=AF=A6?= =?UTF-8?q?=E6=83=85=EF=BC=8C=E6=8A=BD=E5=B1=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/ImageDetailDrawer.vue | 42 ++++++++++++++++++++++++ src/views/ai/image/ImageTask.vue | 29 ++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/views/ai/image/ImageDetailDrawer.vue diff --git a/src/views/ai/image/ImageDetailDrawer.vue b/src/views/ai/image/ImageDetailDrawer.vue new file mode 100644 index 00000000..a4e234e8 --- /dev/null +++ b/src/views/ai/image/ImageDetailDrawer.vue @@ -0,0 +1,42 @@ + + + + diff --git a/src/views/ai/image/ImageTask.vue b/src/views/ai/image/ImageTask.vue index f4c4b9b2..58046f84 100644 --- a/src/views/ai/image/ImageTask.vue +++ b/src/views/ai/image/ImageTask.vue @@ -11,19 +11,42 @@
- +
- + + From c06dd8e273564e6f4c4cd9e582decf15b275271e Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 21:51:42 +0800 Subject: [PATCH 171/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91Ima?= =?UTF-8?q?ge=20Task=20=E4=BD=BF=E7=94=A8=20card=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/ImageTask.vue | 66 +++++++++++--------------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/src/views/ai/image/ImageTask.vue b/src/views/ai/image/ImageTask.vue index 58046f84..a612a85d 100644 --- a/src/views/ai/image/ImageTask.vue +++ b/src/views/ai/image/ImageTask.vue @@ -2,22 +2,7 @@ From cb7c5b745808c82f6df89adf2456f70279d9cd89 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 21:53:26 +0800 Subject: [PATCH 172/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91Ima?= =?UTF-8?q?ge=20=E6=8A=BD=E5=B1=89=EF=BC=8C=E4=BD=BF=E7=94=A8=20Card=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/ImageDetailDrawer.vue | 66 +++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/views/ai/image/ImageDetailDrawer.vue b/src/views/ai/image/ImageDetailDrawer.vue index a4e234e8..7114beda 100644 --- a/src/views/ai/image/ImageDetailDrawer.vue +++ b/src/views/ai/image/ImageDetailDrawer.vue @@ -4,13 +4,35 @@ title="图片详细" @close="handlerDrawerClose" > - Hi, there! +
+
+
图片
+
+ + + +
+
+
+ +
+
+
+
提示词
+
+ {{imageDetail.prompt}} +
+
From 959ee908108357c253ef701a65361d1070aa6c6c Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 26 May 2024 21:56:10 +0800 Subject: [PATCH 173/235] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=20Image=20Task=20Card=20=E6=8C=89=E9=94=AE?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/ImageTask.vue | 11 ++++++++++- src/views/ai/image/ImageTaskCard.vue | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/views/ai/image/ImageTask.vue b/src/views/ai/image/ImageTask.vue index a612a85d..50a2a7ad 100644 --- a/src/views/ai/image/ImageTask.vue +++ b/src/views/ai/image/ImageTask.vue @@ -2,7 +2,7 @@ @@ -24,11 +21,33 @@ import Dall3 from './dall3/index.vue' import Midjourney from './midjourney/index.vue' import ImageTask from './ImageTask.vue' +// ref +const imageTaskRef = ref() // image task ref + // 定义属性 const selectModel = ref('DALL3绘画') const modelOptions = ['DALL3绘画', 'MJ绘画'] +/** + * 绘画 - start + */ +const handlerDrawStart = async (type) => { + // todo +} + +/** + * 绘画 - complete + */ +const handlerDrawComplete = async (type) => { + // todo + await imageTaskRef.value.getImageList() +} + +// +onMounted( async () => { +}) + From ad10328b735a913a82157c978970d4d815248d06 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Tue, 28 May 2024 10:04:44 +0800 Subject: [PATCH 187/235] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI?= =?UTF-8?q?=20Image=20=E5=88=97=E8=A1=A8=E5=A2=9E=E5=8A=A0=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/image/ImageTaskCard.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/ai/image/ImageTaskCard.vue b/src/views/ai/image/ImageTaskCard.vue index e8a73db9..959209e6 100644 --- a/src/views/ai/image/ImageTaskCard.vue +++ b/src/views/ai/image/ImageTaskCard.vue @@ -1,9 +1,11 @@ +