fix: 合并冲突

pull/273/head
hw 2025-11-24 17:52:03 +08:00
parent 241c70ebb0
commit d29e53ff15
222 changed files with 3331 additions and 1115 deletions

View File

@ -4,7 +4,8 @@
"register": "Register", "register": "Register",
"codeLogin": "Code Login", "codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login", "qrcodeLogin": "Qr Code Login",
"forgetPassword": "Forget Password" "forgetPassword": "Forget Password",
"profile": "Profile"
}, },
"dashboard": { "dashboard": {
"title": "Dashboard", "title": "Dashboard",

View File

@ -4,7 +4,8 @@
"register": "注册", "register": "注册",
"codeLogin": "验证码登录", "codeLogin": "验证码登录",
"qrcodeLogin": "二维码登录", "qrcodeLogin": "二维码登录",
"forgetPassword": "忘记密码" "forgetPassword": "忘记密码",
"profile": "个人中心"
}, },
"dashboard": { "dashboard": {
"title": "概览", "title": "概览",

View File

@ -90,7 +90,7 @@ export const useMallKefuStore = defineStore('mall-kefu', {
}, },
conversationSort() { conversationSort() {
// 按置顶属性和最后消息时间排序 // 按置顶属性和最后消息时间排序
this.conversationList.sort((a, b) => { this.conversationList.toSorted((a, b) => {
// 按照置顶排序,置顶的会在前面 // 按照置顶排序,置顶的会在前面
if (a.adminPinned !== b.adminPinned) { if (a.adminPinned !== b.adminPinned) {
return a.adminPinned ? -1 : 1; return a.adminPinned ? -1 : 1;

View File

@ -0,0 +1,102 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import type { VbenFormSchema } from '#/adapter/form';
import type { SystemUserProfileApi } from '#/api/system/user/profile';
import { computed, ref, watch } from 'vue';
import { ProfileBaseSetting, z } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { message } from 'ant-design-vue';
import { updateUserProfile } from '#/api/system/user/profile';
import { $t } from '#/locales';
const props = defineProps<{
profile?: SystemUserProfileApi.UserProfileRespVO;
}>();
const emit = defineEmits<{
(e: 'success'): void;
}>();
const profileBaseSettingRef = ref();
const formSchema = computed((): VbenFormSchema[] => {
return [
{
label: '用户昵称',
fieldName: 'nickname',
component: 'Input',
componentProps: {
placeholder: '请输入用户昵称',
},
rules: 'required',
},
{
label: '用户手机',
fieldName: 'mobile',
component: 'Input',
componentProps: {
placeholder: '请输入用户手机',
},
rules: z.string(),
},
{
label: '用户邮箱',
fieldName: 'email',
component: 'Input',
componentProps: {
placeholder: '请输入用户邮箱',
},
rules: z.string().email('请输入正确的邮箱'),
},
{
label: '用户性别',
fieldName: 'sex',
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
buttonStyle: 'solid',
optionType: 'button',
},
rules: z.number(),
},
];
});
async function handleSubmit(values: Recordable<any>) {
try {
profileBaseSettingRef.value.getFormApi().setLoading(true);
//
await updateUserProfile(values as SystemUserProfileApi.UpdateProfileReqVO);
//
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} catch (error) {
console.error(error);
} finally {
profileBaseSettingRef.value.getFormApi().setLoading(false);
}
}
/** 监听 profile 变化 */
watch(
() => props.profile,
(newProfile) => {
if (newProfile) {
profileBaseSettingRef.value.getFormApi().setValues(newProfile);
}
},
{ immediate: true },
);
</script>
<template>
<ProfileBaseSetting
ref="profileBaseSettingRef"
:form-schema="formSchema"
@submit="handleSubmit"
/>
</template>

View File

@ -0,0 +1,31 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileNotificationSetting } from '@vben/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '其他用户的消息将以站内信的形式通知',
},
{
value: true,
fieldName: 'systemMessage',
label: '系统消息',
description: '系统消息将以站内信的形式通知',
},
{
value: true,
fieldName: 'todoTask',
label: '待办任务',
description: '待办任务将以站内信的形式通知',
},
];
});
</script>
<template>
<ProfileNotificationSetting :form-schema="formSchema" />
</template>

View File

@ -0,0 +1,66 @@
<script setup lang="ts">
import type { VbenFormSchema } from '#/adapter/form';
import { computed, ref } from 'vue';
import { ProfilePasswordSetting, z } from '@vben/common-ui';
import { message } from 'ant-design-vue';
const profilePasswordSettingRef = ref();
const formSchema = computed((): VbenFormSchema[] => {
return [
{
fieldName: 'oldPassword',
label: '旧密码',
component: 'VbenInputPassword',
componentProps: {
placeholder: '请输入旧密码',
},
},
{
fieldName: 'newPassword',
label: '新密码',
component: 'VbenInputPassword',
componentProps: {
passwordStrength: true,
placeholder: '请输入新密码',
},
},
{
fieldName: 'confirmPassword',
label: '确认密码',
component: 'VbenInputPassword',
componentProps: {
passwordStrength: true,
placeholder: '请再次输入新密码',
},
dependencies: {
rules(values) {
const { newPassword } = values;
return z
.string({ required_error: '请再次输入新密码' })
.min(1, { message: '请再次输入新密码' })
.refine((value) => value === newPassword, {
message: '两次输入的密码不一致',
});
},
triggerFields: ['newPassword'],
},
},
];
});
function handleSubmit() {
message.success('密码修改成功');
}
</script>
<template>
<ProfilePasswordSetting
ref="profilePasswordSettingRef"
class="w-1/3"
:form-schema="formSchema"
@submit="handleSubmit"
/>
</template>

View File

@ -0,0 +1,43 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileSecuritySetting } from '@vben/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '当前密码强度:强',
},
{
value: true,
fieldName: 'securityPhone',
label: '密保手机',
description: '已绑定手机138****8293',
},
{
value: true,
fieldName: 'securityQuestion',
label: '密保问题',
description: '未设置密保问题,密保问题可有效保护账户安全',
},
{
value: true,
fieldName: 'securityEmail',
label: '备用邮箱',
description: '已绑定邮箱ant***sign.com',
},
{
value: false,
fieldName: 'securityMfa',
label: 'MFA 设备',
description: '未绑定 MFA 设备,绑定后,可以进行二次确认',
},
];
});
</script>
<template>
<ProfileSecuritySetting :form-schema="formSchema" />
</template>

View File

@ -403,8 +403,8 @@ async function doSendMessageStream(userMessage: AiChatMessageApi.ChatMessage) {
const lastMessage = const lastMessage =
activeMessageList.value[activeMessageList.value.length - 1]; activeMessageList.value[activeMessageList.value.length - 1];
// //
lastMessage.reasoningContent = lastMessage!.reasoningContent =
(lastMessage.reasoningContent || '') + (lastMessage!.reasoningContent || '') +
data.receive.reasoningContent; data.receive.reasoningContent;
} }
@ -554,7 +554,7 @@ onMounted(async () => {
<!-- 右侧详情部分 --> <!-- 右侧详情部分 -->
<Layout class="bg-card mx-4"> <Layout class="bg-card mx-4">
<Layout.Header <Layout.Header
class="!bg-card border-border flex !h-12 items-center justify-between border-b !px-4" class="border-border !bg-card flex !h-12 items-center justify-between border-b !px-4"
> >
<div class="text-lg font-bold"> <div class="text-lg font-bold">
{{ activeConversation?.title ? activeConversation?.title : '对话' }} {{ activeConversation?.title ? activeConversation?.title : '对话' }}

View File

@ -90,7 +90,7 @@ async function getChatConversationList() {
// 1.1 // 1.1
conversationList.value = await getChatConversationMyList(); conversationList.value = await getChatConversationMyList();
// 1.2 // 1.2
conversationList.value.sort((a, b) => { conversationList.value.toSorted((a, b) => {
return Number(b.createTime) - Number(a.createTime); return Number(b.createTime) - Number(a.createTime);
}); });
// 1.3 // 1.3

View File

@ -138,14 +138,14 @@ async function uploadFile(fileItem: FileItem) {
fileItem.progress = 100; fileItem.progress = 100;
// //
console.log('上传响应:', response); console.warn('上传响应:', response);
// { url: '...' } { data: '...' } // { url: '...' } { data: '...' }
const fileUrl = const fileUrl =
(response as any)?.url || (response as any)?.data || response; (response as any)?.url || (response as any)?.data || response;
fileItem.url = fileUrl; fileItem.url = fileUrl;
console.log('提取的文件 URL:', fileUrl); console.warn('提取的文件 URL:', fileUrl);
// URL // URL
if (fileUrl && typeof fileUrl === 'string') { if (fileUrl && typeof fileUrl === 'string') {

View File

@ -13,7 +13,6 @@ import {
getChatRole, getChatRole,
updateChatRole, updateChatRole,
} from '#/api/ai/model/chatRole'; } from '#/api/ai/model/chatRole';
import {} from '#/api/bpm/model';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -9,7 +9,6 @@ import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { createModel, getModel, updateModel } from '#/api/ai/model/model'; import { createModel, getModel, updateModel } from '#/api/ai/model/model';
import {} from '#/api/bpm/model';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -17,7 +17,7 @@ const currentSong = ref({}); // 当前音乐
const mySongList = ref<Recordable<any>[]>([]); const mySongList = ref<Recordable<any>[]>([]);
const squareSongList = ref<Recordable<any>[]>([]); const squareSongList = ref<Recordable<any>[]>([]);
function generateMusic(formData: Recordable<any>) { function generateMusic(_formData: Recordable<any>) {
loading.value = true; loading.value = true;
setTimeout(() => { setTimeout(() => {
mySongList.value = Array.from({ length: 20 }, (_, index) => { mySongList.value = Array.from({ length: 20 }, (_, index) => {

View File

@ -21,7 +21,7 @@ const emits = defineEmits<{
<span <span
v-for="tag in props.tags" v-for="tag in props.tags"
:key="tag.value" :key="tag.value"
class="bg-card border-card-100 mb-2 cursor-pointer rounded border-2 border-solid px-1 text-xs leading-6" class="border-card-100 bg-card mb-2 cursor-pointer rounded border-2 border-solid px-1 text-xs leading-6"
:class=" :class="
modelValue === tag.value && '!border-primary-500 !text-primary-500' modelValue === tag.value && '!border-primary-500 !text-primary-500'
" "

View File

@ -62,16 +62,16 @@ const resetElement = () => {
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] }); bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] });
// //
boundaryEventType.value = elExtensionElements.value.values?.filter( boundaryEventType.value = elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:BoundaryEventType`, (ex: any) => ex.$type === `${prefix}:BoundaryEventType`,
)?.[0]; );
if (boundaryEventType.value && boundaryEventType.value.value === 1) { if (boundaryEventType.value && boundaryEventType.value.value === 1) {
timeoutHandlerEnable.value = true; timeoutHandlerEnable.value = true;
configExtensions.value.push(boundaryEventType.value); configExtensions.value.push(boundaryEventType.value);
} }
// //
timeoutHandlerType.value = elExtensionElements.value.values?.filter( timeoutHandlerType.value = elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:TimeoutHandlerType`, (ex: any) => ex.$type === `${prefix}:TimeoutHandlerType`,
)?.[0]; )?.[0];
if (timeoutHandlerType.value) { if (timeoutHandlerType.value) {

View File

@ -112,7 +112,7 @@ const resetCustomConfigList = () => {
// //
approveType.value = approveType.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:ApproveType`, (ex: any) => ex.$type === `${prefix}:ApproveType`,
)?.[0] || )?.[0] ||
bpmnInstances().moddle.create(`${prefix}:ApproveType`, { bpmnInstances().moddle.create(`${prefix}:ApproveType`, {
@ -121,7 +121,7 @@ const resetCustomConfigList = () => {
// //
assignStartUserHandlerTypeEl.value = assignStartUserHandlerTypeEl.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:AssignStartUserHandlerType`, (ex: any) => ex.$type === `${prefix}:AssignStartUserHandlerType`,
)?.[0] || )?.[0] ||
bpmnInstances().moddle.create(`${prefix}:AssignStartUserHandlerType`, { bpmnInstances().moddle.create(`${prefix}:AssignStartUserHandlerType`, {
@ -131,13 +131,13 @@ const resetCustomConfigList = () => {
// //
rejectHandlerTypeEl.value = rejectHandlerTypeEl.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:RejectHandlerType`, (ex: any) => ex.$type === `${prefix}:RejectHandlerType`,
)?.[0] || )?.[0] ||
bpmnInstances().moddle.create(`${prefix}:RejectHandlerType`, { value: 1 }); bpmnInstances().moddle.create(`${prefix}:RejectHandlerType`, { value: 1 });
rejectHandlerType.value = rejectHandlerTypeEl.value.value; rejectHandlerType.value = rejectHandlerTypeEl.value.value;
returnNodeIdEl.value = returnNodeIdEl.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:RejectReturnTaskId`, (ex: any) => ex.$type === `${prefix}:RejectReturnTaskId`,
)?.[0] || )?.[0] ||
bpmnInstances().moddle.create(`${prefix}:RejectReturnTaskId`, { bpmnInstances().moddle.create(`${prefix}:RejectReturnTaskId`, {
@ -147,7 +147,7 @@ const resetCustomConfigList = () => {
// //
assignEmptyHandlerTypeEl.value = assignEmptyHandlerTypeEl.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:AssignEmptyHandlerType`, (ex: any) => ex.$type === `${prefix}:AssignEmptyHandlerType`,
)?.[0] || )?.[0] ||
bpmnInstances().moddle.create(`${prefix}:AssignEmptyHandlerType`, { bpmnInstances().moddle.create(`${prefix}:AssignEmptyHandlerType`, {
@ -155,7 +155,7 @@ const resetCustomConfigList = () => {
}); });
assignEmptyHandlerType.value = assignEmptyHandlerTypeEl.value.value; assignEmptyHandlerType.value = assignEmptyHandlerTypeEl.value.value;
assignEmptyUserIdsEl.value = assignEmptyUserIdsEl.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:AssignEmptyUserIds`, (ex: any) => ex.$type === `${prefix}:AssignEmptyUserIds`,
)?.[0] || )?.[0] ||
bpmnInstances().moddle.create(`${prefix}:AssignEmptyUserIds`, { bpmnInstances().moddle.create(`${prefix}:AssignEmptyUserIds`, {
@ -172,7 +172,7 @@ const resetCustomConfigList = () => {
}); });
// //
buttonsSettingEl.value = elExtensionElements.value.values?.filter( buttonsSettingEl.value = elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:ButtonsSetting`, (ex: any) => ex.$type === `${prefix}:ButtonsSetting`,
); );
if (buttonsSettingEl.value.length === 0) { if (buttonsSettingEl.value.length === 0) {
@ -189,7 +189,7 @@ const resetCustomConfigList = () => {
// //
if (formType.value === BpmModelFormType.NORMAL) { if (formType.value === BpmModelFormType.NORMAL) {
const fieldsPermissionList = elExtensionElements.value.values?.filter( const fieldsPermissionList = elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:FieldsPermission`, (ex: any) => ex.$type === `${prefix}:FieldsPermission`,
); );
fieldsPermissionEl.value = []; fieldsPermissionEl.value = [];
@ -206,21 +206,21 @@ const resetCustomConfigList = () => {
// //
signEnable.value = signEnable.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:SignEnable`, (ex: any) => ex.$type === `${prefix}:SignEnable`,
)?.[0] || ) ||
bpmnInstances().moddle.create(`${prefix}:SignEnable`, { value: false }); bpmnInstances().moddle.create(`${prefix}:SignEnable`, { value: false });
// //
reasonRequire.value = reasonRequire.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => ex.$type === `${prefix}:ReasonRequire`, (ex: any) => ex.$type === `${prefix}:ReasonRequire`,
)?.[0] || ) ||
bpmnInstances().moddle.create(`${prefix}:ReasonRequire`, { value: false }); bpmnInstances().moddle.create(`${prefix}:ReasonRequire`, { value: false });
// 便 // 便
otherExtensions.value = otherExtensions.value =
elExtensionElements.value.values?.filter( elExtensionElements.value.values?.find(
(ex: any) => (ex: any) =>
ex.$type !== `${prefix}:AssignStartUserHandlerType` && ex.$type !== `${prefix}:AssignStartUserHandlerType` &&
ex.$type !== `${prefix}:RejectHandlerType` && ex.$type !== `${prefix}:RejectHandlerType` &&

View File

@ -118,10 +118,10 @@ const resetTaskForm = () => {
const extensionElements = const extensionElements =
businessObject?.extensionElements ?? businessObject?.extensionElements ??
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] }); bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] });
userTaskForm.value.candidateStrategy = extensionElements.values?.filter( userTaskForm.value.candidateStrategy = extensionElements.values?.find(
(ex: any) => ex.$type === `${prefix}:CandidateStrategy`, (ex: any) => ex.$type === `${prefix}:CandidateStrategy`,
)?.[0]?.value; )?.[0]?.value;
const candidateParamStr = extensionElements.values?.filter( const candidateParamStr = extensionElements.values?.find(
(ex: any) => ex.$type === `${prefix}:CandidateParam`, (ex: any) => ex.$type === `${prefix}:CandidateParam`,
)?.[0]?.value; )?.[0]?.value;
if (candidateParamStr && candidateParamStr.length > 0) { if (candidateParamStr && candidateParamStr.length > 0) {

View File

@ -112,7 +112,7 @@ function setDuration(type, val) {
// ISO 8601 // ISO 8601
let d = isoDuration.value; let d = isoDuration.value;
if (d.includes(type)) { if (d.includes(type)) {
d = d.replace(new RegExp(`\\d+${type}`), val + type); d = d.replace(new RegExp(String.raw`\d+${type}`), val + type);
} else { } else {
d += val + type; d += val + type;
} }

View File

@ -82,10 +82,12 @@ export function updateElementExtensions(element, extensionList) {
} }
// 创建一个id // 创建一个id
export function uuid(length = 8, chars?) { export function uuid(
length = 8,
charsString = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
) {
let result = ''; let result = '';
const charsString =
chars || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i = length; i > 0; --i) { for (let i = length; i > 0; --i) {
result += charsString[Math.floor(Math.random() * charsString.length)]; result += charsString[Math.floor(Math.random() * charsString.length)];
} }

View File

@ -6,7 +6,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -54,7 +54,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -102,7 +102,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -150,7 +150,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -198,7 +198,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -246,7 +246,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -294,7 +294,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -342,7 +342,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,

View File

@ -24,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 }, { name: '定制', value: 310 },
{ name: '技术支持', value: 274 }, { name: '技术支持', value: 274 },
{ name: '远程', value: 400 }, { name: '远程', value: 400 },
].sort((a, b) => { ].toSorted((a, b) => {
return a.value - b.value; return a.value - b.value;
}), }),
name: '商业占比', name: '商业占比',

View File

@ -128,6 +128,7 @@ function handleOpenSaleOut() {
function handleAddSaleOut(rows: ErpSaleOutApi.SaleOut[]) { function handleAddSaleOut(rows: ErpSaleOutApi.SaleOut[]) {
rows.forEach((row) => { rows.forEach((row) => {
// TODO
const newItem: ErpFinanceReceiptApi.FinanceReceiptItem = { const newItem: ErpFinanceReceiptApi.FinanceReceiptItem = {
bizId: row.id, bizId: row.id,
bizType: ErpBizType.SALE_OUT, bizType: ErpBizType.SALE_OUT,
@ -153,6 +154,7 @@ function handleOpenSaleReturn() {
} }
function handleAddSaleReturn(rows: ErpSaleReturnApi.SaleReturn[]) { function handleAddSaleReturn(rows: ErpSaleReturnApi.SaleReturn[]) {
// TODO
rows.forEach((row) => { rows.forEach((row) => {
const newItem: ErpFinanceReceiptApi.FinanceReceiptItem = { const newItem: ErpFinanceReceiptApi.FinanceReceiptItem = {
bizId: row.id, bizId: row.id,

View File

@ -151,6 +151,7 @@ async function handleWarehouseChange(row: ErpPurchaseInApi.PurchaseInItem) {
/** 处理行数据变更 */ /** 处理行数据变更 */
function handleRowChange(row: any) { function handleRowChange(row: any) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index === -1) { if (index === -1) {
tableData.value.push(row); tableData.value.push(row);

View File

@ -142,6 +142,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrderItem) { function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrderItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpPurchaseReturnApi.PurchaseReturnItem) { function handleDelete(row: ErpPurchaseReturnApi.PurchaseReturnItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);
@ -153,6 +154,7 @@ async function handleWarehouseChange(
/** 处理行数据变更 */ /** 处理行数据变更 */
function handleRowChange(row: any) { function handleRowChange(row: any) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index === -1) { if (index === -1) {
tableData.value.push(row); tableData.value.push(row);

View File

@ -142,6 +142,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpSaleOrderApi.SaleOrderItem) { function handleDelete(row: ErpSaleOrderApi.SaleOrderItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpSaleOutApi.SaleOutItem) { function handleDelete(row: ErpSaleOutApi.SaleOutItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpSaleReturnApi.SaleReturnItem) { function handleDelete(row: ErpSaleReturnApi.SaleReturnItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -106,6 +106,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpStockCheckApi.StockCheckItem) { function handleDelete(row: ErpStockCheckApi.StockCheckItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -98,6 +98,7 @@ function handleAdd() {
totalPrice: undefined, totalPrice: undefined,
remark: undefined, remark: undefined,
}; };
// TODO
tableData.value.push(newRow); tableData.value.push(newRow);
// //
emit('update:items', [...tableData.value]); emit('update:items', [...tableData.value]);
@ -105,6 +106,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpStockInApi.StockInItem) { function handleDelete(row: ErpStockInApi.StockInItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -106,6 +106,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpStockMoveApi.StockMoveItem) { function handleDelete(row: ErpStockMoveApi.StockMoveItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -105,6 +105,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpStockOutApi.StockOutItem) { function handleDelete(row: ErpStockOutApi.StockOutItem) {
// TODO
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -47,7 +47,7 @@ const { status, data, send, close, open } = useWebSocket(server.value, {
const messageList = ref( const messageList = ref(
[] as { text: string; time: number; type?: string; userId?: string }[], [] as { text: string; time: number; type?: string; userId?: string }[],
); // ); //
const messageReverseList = computed(() => [...messageList.value].reverse()); const messageReverseList = computed(() => [...messageList.value].toReversed());
watchEffect(() => { watchEffect(() => {
if (!data.value) { if (!data.value) {
return; return;

View File

@ -553,17 +553,17 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗
.toolbar-wrapper { .toolbar-wrapper {
padding: 16px; padding: 16px;
background-color: hsl(var(--card) / 0.9); background-color: hsl(var(--card) / 90%);
border: 1px solid hsl(var(--border) / 60%);
border-radius: 8px; border-radius: 8px;
border: 1px solid hsl(var(--border) / 0.6);
} }
.chart-container, .chart-container,
.table-container { .table-container {
padding: 16px; padding: 16px;
background-color: hsl(var(--card)); background-color: hsl(var(--card));
border: 1px solid hsl(var(--border) / 60%);
border-radius: 8px; border-radius: 8px;
border: 1px solid hsl(var(--border) / 0.6);
} }
} }
</style> </style>

View File

@ -22,8 +22,7 @@ import {
import { getLatestDeviceProperties } from '#/api/iot/device/device'; import { getLatestDeviceProperties } from '#/api/iot/device/device';
import DeviceDetailsThingModelPropertyHistory import DeviceDetailsThingModelPropertyHistory from './device-details-thing-model-property-history.vue';
from './device-details-thing-model-property-history.vue';
const props = defineProps<{ deviceId: number }>(); const props = defineProps<{ deviceId: number }>();

View File

@ -176,7 +176,7 @@ function getDeviceTypeColor(deviceType: number) {
} }
// //
function getStatusInfo(state: number | string | null | undefined) { function getStatusInfo(state: null | number | string | undefined) {
const parsedState = Number(state); const parsedState = Number(state);
const hasNumericState = Number.isFinite(parsedState); const hasNumericState = Number.isFinite(parsedState);
const fallback = hasNumericState const fallback = hasNumericState
@ -396,21 +396,21 @@ defineExpose({
.device-card { .device-card {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
background: hsl(var(--card) / 0.95); background: hsl(var(--card) / 95%);
border: 1px solid hsl(var(--border) / 0.6); border: 1px solid hsl(var(--border) / 60%);
border-radius: 8px; border-radius: 8px;
box-shadow: box-shadow:
0 1px 2px 0 hsl(var(--foreground) / 0.04), 0 1px 2px 0 hsl(var(--foreground) / 4%),
0 1px 6px -1px hsl(var(--foreground) / 0.05), 0 1px 6px -1px hsl(var(--foreground) / 5%),
0 2px 4px 0 hsl(var(--foreground) / 0.05); 0 2px 4px 0 hsl(var(--foreground) / 5%);
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover { &:hover {
border-color: hsl(var(--border)); border-color: hsl(var(--border));
box-shadow: box-shadow:
0 1px 2px -2px hsl(var(--foreground) / 0.12), 0 1px 2px -2px hsl(var(--foreground) / 12%),
0 3px 6px 0 hsl(var(--foreground) / 0.1), 0 3px 6px 0 hsl(var(--foreground) / 10%),
0 5px 12px 4px hsl(var(--foreground) / 0.08); 0 5px 12px 4px hsl(var(--foreground) / 8%);
transform: translateY(-4px); transform: translateY(-4px);
} }
@ -473,7 +473,7 @@ defineExpose({
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
line-height: 24px; line-height: 24px;
color: hsl(var(--foreground) / 0.9); color: hsl(var(--foreground) / 90%);
white-space: nowrap; white-space: nowrap;
} }
@ -496,7 +496,7 @@ defineExpose({
.label { .label {
flex-shrink: 0; flex-shrink: 0;
font-size: 13px; font-size: 13px;
color: hsl(var(--foreground) / 0.6); color: hsl(var(--foreground) / 60%);
} }
.value { .value {
@ -505,7 +505,7 @@ defineExpose({
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
font-size: 13px; font-size: 13px;
color: hsl(var(--foreground) / 0.85); color: hsl(var(--foreground) / 85%);
text-align: right; text-align: right;
white-space: nowrap; white-space: nowrap;
@ -515,7 +515,7 @@ defineExpose({
transition: color 0.2s; transition: color 0.2s;
&:hover { &:hover {
color: hsl(var(--primary) / 0.85); color: hsl(var(--primary) / 85%);
} }
} }
@ -524,7 +524,7 @@ defineExpose({
'SF Mono', Monaco, Inconsolata, 'Fira Code', Consolas, monospace; 'SF Mono', Monaco, Inconsolata, 'Fira Code', Consolas, monospace;
font-size: 12px; font-size: 12px;
font-weight: 500; font-weight: 500;
color: hsl(var(--foreground) / 0.6); color: hsl(var(--foreground) / 60%);
} }
} }
} }
@ -537,7 +537,7 @@ defineExpose({
display: flex; display: flex;
gap: 8px; gap: 8px;
padding-top: 12px; padding-top: 12px;
border-top: 1px solid hsl(var(--border) / 0.4); border-top: 1px solid hsl(var(--border) / 40%);
.action-btn { .action-btn {
display: flex; display: flex;
@ -561,8 +561,8 @@ defineExpose({
&.btn-edit { &.btn-edit {
color: hsl(var(--primary)); color: hsl(var(--primary));
background: hsl(var(--primary) / 0.12); background: hsl(var(--primary) / 12%);
border-color: hsl(var(--primary) / 0.25); border-color: hsl(var(--primary) / 25%);
&:hover { &:hover {
color: hsl(var(--primary-foreground)); color: hsl(var(--primary-foreground));
@ -573,8 +573,8 @@ defineExpose({
&.btn-view { &.btn-view {
color: hsl(var(--warning)); color: hsl(var(--warning));
background: hsl(var(--warning) / 0.12); background: hsl(var(--warning) / 12%);
border-color: hsl(var(--warning) / 0.25); border-color: hsl(var(--warning) / 25%);
&:hover { &:hover {
color: #fff; color: #fff;
@ -590,11 +590,7 @@ defineExpose({
hsl(var(--accent)) 40%, hsl(var(--accent)) 40%,
hsl(var(--card)) 60% hsl(var(--card)) 60%
); );
border-color: color-mix( border-color: color-mix(in srgb, hsl(var(--accent)) 55%, transparent);
in srgb,
hsl(var(--accent)) 55%,
transparent
);
&:hover { &:hover {
color: hsl(var(--accent-foreground)); color: hsl(var(--accent-foreground));
@ -607,8 +603,8 @@ defineExpose({
flex: 0 0 32px; flex: 0 0 32px;
padding: 4px; padding: 4px;
color: hsl(var(--destructive)); color: hsl(var(--destructive));
background: hsl(var(--destructive) / 0.12); background: hsl(var(--destructive) / 12%);
border-color: hsl(var(--destructive) / 0.3); border-color: hsl(var(--destructive) / 30%);
&:hover { &:hover {
color: hsl(var(--destructive-foreground)); color: hsl(var(--destructive-foreground));

View File

@ -1,12 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import {ComparisonCard, Page} from '@vben/common-ui'; // TODO @
import type { StatsData } from './data';
import {Col, Row} from 'ant-design-vue'; import { onMounted, ref } from 'vue';
import {onMounted, ref} from 'vue';
import {getStatisticsSummary} from '#/api/iot/statistics'; import { ComparisonCard, Page } from '@vben/common-ui';
import {defaultStatsData, type StatsData} from './data'; import { Col, Row } from 'ant-design-vue';
import { getStatisticsSummary } from '#/api/iot/statistics';
import { defaultStatsData } from './data';
import DeviceCountCard from './modules/device-count-card.vue'; import DeviceCountCard from './modules/device-count-card.vue';
import DeviceStateCountCard from './modules/device-state-count-card.vue'; import DeviceStateCountCard from './modules/device-state-count-card.vue';
import MessageTrendCard from './modules/message-trend-card.vue'; import MessageTrendCard from './modules/message-trend-card.vue';

View File

@ -140,9 +140,9 @@ onMounted(() => {
<span class="text-base font-medium text-gray-600">消息量统计</span> <span class="text-base font-medium text-gray-600">消息量统计</span>
<div class="flex flex-wrap items-center gap-4"> <div class="flex flex-wrap items-center gap-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="whitespace-nowrap text-sm text-gray-500" <span class="whitespace-nowrap text-sm text-gray-500">
>时间范围</span 时间范围
> </span>
<ShortcutDateRangePicker @change="handleDateRangeChange" /> <ShortcutDateRangePicker @change="handleDateRangeChange" />
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@ -178,4 +178,4 @@ onMounted(() => {
<EchartsUI ref="messageChartRef" class="h-[300px] w-full" /> <EchartsUI ref="messageChartRef" class="h-[300px] w-full" />
</div> </div>
</Card> </Card>
</template> </template>

View File

@ -1,6 +1,6 @@
export {default as HttpConfigForm} from './http-config-form.vue'; export { default as HttpConfigForm } from './http-config-form.vue';
export {default as KafkaMqConfigForm} from './kafka-mq-config-form.vue'; export { default as KafkaMqConfigForm } from './kafka-mq-config-form.vue';
export {default as MqttConfigForm} from './mqtt-config-form.vue'; export { default as MqttConfigForm } from './mqtt-config-form.vue';
export {default as RabbitMqConfigForm} from './rabbit-mq-config-form.vue'; export { default as RabbitMqConfigForm } from './rabbit-mq-config-form.vue';
export {default as RedisStreamConfigForm} from './redis-stream-config-form.vue'; export { default as RedisStreamConfigForm } from './redis-stream-config-form.vue';
export {default as RocketMqConfigForm} from './rocket-mq-config-form.vue'; export { default as RocketMqConfigForm } from './rocket-mq-config-form.vue';

View File

@ -135,7 +135,8 @@ function handleDeviceChange(_: any) {
/** /**
* 处理属性变化事件 * 处理属性变化事件
* @param propertyInfo 属性信息对象 * @param propertyInfo.config 属性配置
* @param propertyInfo.type 属性类型
*/ */
function handlePropertyChange(propertyInfo: { config: any; type: string }) { function handlePropertyChange(propertyInfo: { config: any; type: string }) {
propertyType.value = propertyInfo.type; propertyType.value = propertyInfo.type;

View File

@ -108,10 +108,10 @@ function updateCondition(index: number, condition: TriggerCondition) {
> >
<!-- 条件配置 --> <!-- 条件配置 -->
<div <div
class="rounded-3px border-border bg-fill-color-blank border shadow-sm" class="rounded-3px bg-fill-color-blank border-border border shadow-sm"
> >
<div <div
class="rounded-t-1 border-border bg-fill-color-blank flex items-center justify-between border-b p-3" class="rounded-t-1 bg-fill-color-blank border-border flex items-center justify-between border-b p-3"
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div <div

View File

@ -507,7 +507,7 @@ watch(
{{ JSON_PARAMS_INPUT_CONSTANTS.COMPLETE_JSON_FORMAT }} {{ JSON_PARAMS_INPUT_CONSTANTS.COMPLETE_JSON_FORMAT }}
</div> </div>
<pre <pre
class="bg-card border-l-3px border-primary text-primary overflow-x-auto rounded-lg p-3 text-sm" class="border-l-3px border-primary bg-card text-primary overflow-x-auto rounded-lg p-3 text-sm"
> >
<code>{{ generateExampleJson() }}</code> <code>{{ generateExampleJson() }}</code>
</pre> </pre>

View File

@ -275,7 +275,7 @@ function onActionTypeChange(action: Action, type: any) {
action.type === action.type ===
IotRuleSceneActionTypeEnum.ALERT_TRIGGER.toString() IotRuleSceneActionTypeEnum.ALERT_TRIGGER.toString()
" "
class="border-border bg-fill-color-blank rounded-lg border p-4" class="bg-fill-color-blank border-border rounded-lg border p-4"
> >
<div class="mb-2 flex items-center gap-2"> <div class="mb-2 flex items-center gap-2">
<IconifyIcon icon="ep:warning" class="text-warning text-base" /> <IconifyIcon icon="ep:warning" class="text-warning text-base" />

View File

@ -8,7 +8,8 @@ import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { Card, Col, Form, Input, Radio, Row } from 'ant-design-vue'; import { Card, Col, Form, Input, Radio, Row } from 'ant-design-vue';
import { DictTag } from "#/components/dict-tag";
import { DictTag } from '#/components/dict-tag';
/** 基础信息配置组件 */ /** 基础信息配置组件 */
defineOptions({ name: 'BasicInfoSection' }); defineOptions({ name: 'BasicInfoSection' });

View File

@ -7,7 +7,7 @@ import { DICT_TYPE } from '@vben/constants';
import { Select } from 'ant-design-vue'; import { Select } from 'ant-design-vue';
import { getSimpleProductList } from '#/api/iot/product/product'; import { getSimpleProductList } from '#/api/iot/product/product';
import { DictTag } from "#/components/dict-tag"; import { DictTag } from '#/components/dict-tag';
/** 产品选择器组件 */ /** 产品选择器组件 */
defineOptions({ name: 'ProductSelector' }); defineOptions({ name: 'ProductSelector' });
@ -78,7 +78,7 @@ onMounted(() => {
{{ product.productKey }} {{ product.productKey }}
</div> </div>
</div> </div>
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" /> <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
</div> </div>
</Select.Option> </Select.Option>
</Select> </Select>

View File

@ -41,6 +41,7 @@ const emit = defineEmits<{
(e: 'change', value: { config: any; type: string }): void; (e: 'change', value: { config: any; type: string }): void;
}>(); }>();
// TODO
/** 属性选择器内部使用的统一数据结构 */ /** 属性选择器内部使用的统一数据结构 */
interface PropertySelectorItem { interface PropertySelectorItem {
identifier: string; identifier: string;

View File

@ -1,4 +1,4 @@
export {default as ThingModelArrayDataSpecs} from './thing-model-array-data-specs.vue'; export { default as ThingModelArrayDataSpecs } from './thing-model-array-data-specs.vue';
export {default as ThingModelEnumDataSpecs} from './thing-model-enum-data-specs.vue'; export { default as ThingModelEnumDataSpecs } from './thing-model-enum-data-specs.vue';
export {default as ThingModelNumberDataSpecs} from './thing-model-number-data-specs.vue'; export { default as ThingModelNumberDataSpecs } from './thing-model-number-data-specs.vue';
export {default as ThingModelStructDataSpecs} from './thing-model-struct-data-specs.vue'; export { default as ThingModelStructDataSpecs } from './thing-model-struct-data-specs.vue';

View File

@ -126,7 +126,8 @@ function validateSku() {
/** /**
* 选择时触发 * 选择时触发
* *
* @param records 传递过来的选中的 sku 是一个数组 * @param {object} param0 参数对象
* @param {MallSpuApi.Sku[]} param0.records 传递过来的选中的 sku 是一个数组
*/ */
function handleSelectionChange({ records }: { records: MallSpuApi.Sku[] }) { function handleSelectionChange({ records }: { records: MallSpuApi.Sku[] }) {
emit('selectionChange', records); emit('selectionChange', records);

View File

@ -111,6 +111,7 @@ function emitActivityChange() {
> >
<Tooltip :title="activity.name"> <Tooltip :title="activity.name">
<div class="relative h-full w-full"> <div class="relative h-full w-full">
<!-- TODO @芋艿 -->
<Image <Image
:src="activity.picUrl" :src="activity.picUrl"
class="h-full w-full rounded-lg object-cover" class="h-full w-full rounded-lg object-cover"

View File

@ -99,8 +99,8 @@ function handleAppLinkSelected(appLink: AppLink) {
/** /**
* 处理右侧链接列表滚动 * 处理右侧链接列表滚动
* *
* @param {object} param0 滚动事件参数 * @param {Event} event 滚动事件
* @param {number} param0.scrollTop 滚动条的位置 * @param {number} event.target.scrollTop 滚动条的位置
*/ */
function handleScroll(event: Event) { function handleScroll(event: Event) {
const scrollTop = (event.target as HTMLDivElement).scrollTop; const scrollTop = (event.target as HTMLDivElement).scrollTop;

View File

@ -202,7 +202,7 @@ function eachCube(callback: (x: number, y: number, cube: Cube) => void) {
<td <td
v-for="(cube, col) in rowCubes" v-for="(cube, col) in rowCubes"
:key="col" :key="col"
class="active:bg-primary-200 hover:bg-primary-100 box-border cursor-pointer border text-center align-middle" class="hover:bg-primary-100 active:bg-primary-200 box-border cursor-pointer border text-center align-middle"
:class="[{ active: cube.active }]" :class="[{ active: cube.active }]"
:style="{ :style="{
width: `${cubeSize}px`, width: `${cubeSize}px`,
@ -219,7 +219,7 @@ function eachCube(callback: (x: number, y: number, cube: Cube) => void) {
<div <div
v-for="(hotArea, index) in hotAreas" v-for="(hotArea, index) in hotAreas"
:key="index" :key="index"
class="bg-primary-200 border-primary absolute box-border flex items-center justify-center border" class="border-primary bg-primary-200 absolute box-border flex items-center justify-center border"
:style="{ :style="{
top: `${cubeSize * hotArea.top}px`, top: `${cubeSize * hotArea.top}px`,
left: `${cubeSize * hotArea.left}px`, left: `${cubeSize * hotArea.left}px`,

View File

@ -57,10 +57,10 @@ export function isContains(hotArea: Rect, point: Point): boolean {
*/ */
export function createRect(a: Point, b: Point): Rect { export function createRect(a: Point, b: Point): Rect {
// 计算矩形的范围 // 计算矩形的范围
let [left, left2] = [a.x, b.x].sort(); let [left, left2] = [a.x, b.x].toSorted();
left = left ?? 0; left = left ?? 0;
left2 = left2 ?? 0; left2 = left2 ?? 0;
let [top, top2] = [a.y, b.y].sort(); let [top, top2] = [a.y, b.y].toSorted();
top = top ?? 0; top = top ?? 0;
top2 = top2 ?? 0; top2 = top2 ?? 0;
const right = left2 + 1; const right = left2 + 1;

View File

@ -91,7 +91,7 @@ function pushMessage(message: any) {
/** 按照时间倒序,获取消息列表 */ /** 按照时间倒序,获取消息列表 */
const getMessageList0 = computed(() => { const getMessageList0 = computed(() => {
// 使 // 使
return [...messageList.value].sort( return [...messageList.value].toSorted(
(a: any, b: any) => a.createTime - b.createTime, (a: any, b: any) => a.createTime - b.createTime,
); );
}); });

View File

@ -12,7 +12,9 @@ import { getRangePickerDefaultProps } from '#/utils';
/** 关联数据 */ /** 关联数据 */
const userStore = useUserStore(); const userStore = useUserStore();
const pickUpStoreList = ref<MallDeliveryPickUpStoreApi.DeliveryPickUpStore[]>([]); const pickUpStoreList = ref<MallDeliveryPickUpStoreApi.DeliveryPickUpStore[]>(
[],
);
getSimpleDeliveryPickUpStoreList().then((res) => { getSimpleDeliveryPickUpStoreList().then((res) => {
pickUpStoreList.value = res; pickUpStoreList.value = res;
// 移除自己无法核销的门店 // 移除自己无法核销的门店

View File

@ -113,7 +113,8 @@ const [Modal, modalApi] = useVbenModal({
return; return;
} }
// //
const data = modalApi.getData<MallDeliveryPickUpStoreApi.DeliveryPickUpStore>(); const data =
modalApi.getData<MallDeliveryPickUpStoreApi.DeliveryPickUpStore>();
if (!data || !data.id) { if (!data || !data.id) {
// //
await initTencentLbsMap(); await initTencentLbsMap();

View File

@ -111,20 +111,22 @@ const [Modal, modalApi] = useVbenModal({
if (data.row?.id) { if (data.row?.id) {
// //
const rowData = data.row; const rowData = data.row;
const formValues: any = { ...rowData }; const formValues: any = {
formValues.reply = { ...rowData,
type: rowData.responseMessageType, reply: {
accountId: data.accountId || -1, type: rowData.responseMessageType,
content: rowData.responseContent, accountId: data.accountId || -1,
mediaId: rowData.responseMediaId, content: rowData.responseContent,
url: rowData.responseMediaUrl, mediaId: rowData.responseMediaId,
title: rowData.responseTitle, url: rowData.responseMediaUrl,
description: rowData.responseDescription, title: rowData.responseTitle,
thumbMediaId: rowData.responseThumbMediaId, description: rowData.responseDescription,
thumbMediaUrl: rowData.responseThumbMediaUrl, thumbMediaId: rowData.responseThumbMediaId,
articles: rowData.responseArticles, thumbMediaUrl: rowData.responseThumbMediaUrl,
musicUrl: rowData.responseMusicUrl, articles: rowData.responseArticles,
hqMusicUrl: rowData.responseHqMusicUrl, musicUrl: rowData.responseMusicUrl,
hqMusicUrl: rowData.responseHqMusicUrl,
},
}; };
await formApi.setValues(formValues); await formApi.setValues(formValues);
} else { } else {

View File

@ -106,7 +106,7 @@ async function getPage(page: any, params: any = null) {
const scrollHeight = msgDivRef.value?.scrollHeight ?? 0; const scrollHeight = msgDivRef.value?.scrollHeight ?? 0;
// //
const data = dataTemp.list.reverse(); const data = dataTemp.list.toReversed();
list.value = [...data, ...list.value]; list.value = [...data, ...list.value];
loading.value = false; loading.value = false;
if (data.length < queryParams.pageSize || data.length === 0) { if (data.length < queryParams.pageSize || data.length === 0) {

View File

@ -72,12 +72,12 @@ async function handleDelete(row: MpDraftApi.DraftArticle) {
return; return;
} }
const hideLoading = message.loading({ const hideLoading = message.loading({
content: '删除中...', content: $t('ui.actionMessage.deleting'),
duration: 0, duration: 0,
}); });
try { try {
await deleteDraft(accountId, row.mediaId); await deleteDraft(accountId, row.mediaId);
message.success('删除成功'); message.success($t('ui.actionMessage.deleteSuccess'));
handleRefresh(); handleRefresh();
} finally { } finally {
hideLoading(); hideLoading();

View File

@ -0,0 +1,59 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
import type { MpAccountApi } from '#/api/mp/account';
import { formatDateTime } from '@vben/utils';
import { getSimpleAccountList } from '#/api/mp/account';
let accountList: MpAccountApi.AccountSimple[] = [];
getSimpleAccountList().then((data) => (accountList = data));
/** 搜索表单配置 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'accountId',
label: '公众号',
component: 'Select',
componentProps: {
options: accountList.map((item) => ({
label: item.name,
value: item.id,
})),
placeholder: '请选择公众号',
clearable: true,
},
},
];
}
/** 表格列配置 */
export function useGridColumns(): VxeGridPropTypes.Columns {
return [
{
field: 'cover',
title: '图片',
width: 360,
slots: { default: 'cover' },
},
{
field: 'title',
title: '标题',
slots: { default: 'title' },
},
{
field: 'updateTime',
title: '修改时间',
formatter: ({ row }) => {
return formatDateTime(row.updateTime * 1000);
},
},
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@ -1,29 +1,173 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MpFreePublishApi } from '#/api/mp/freePublish';
import { DocAlert, Page } from '@vben/common-ui'; import { DocAlert, Page } from '@vben/common-ui';
import { Button } from 'ant-design-vue'; import { Image, message, Typography } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteFreePublish, getFreePublishPage } from '#/api/mp/freePublish';
import { $t } from '#/locales';
import { WxAccountSelect } from '#/views/mp/components';
import { useGridColumns, useGridFormSchema } from './data';
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 公众号变化时查询数据 */
function handleAccountChange(accountId: number) {
gridApi.formApi.setValues({ accountId });
gridApi.formApi.submitForm();
}
/** 删除文章 */
async function handleDelete(row: MpFreePublishApi.FreePublish) {
const formValues = await gridApi.formApi.getValues();
const accountId = formValues.accountId;
if (!accountId) {
message.warning('请先选择公众号');
return;
}
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting'),
duration: 0,
});
try {
await deleteFreePublish(accountId, row.articleId!);
message.success($t('ui.actionMessage.deleteSuccess'));
handleRefresh();
} finally {
hideLoading();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
submitOnChange: true,
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
if (!formValues.accountId) {
return {
list: [],
total: 0,
};
}
const res = await getFreePublishPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
// thumbUrl picUrl wx-news
res.list.forEach((record: any) => {
const newsList = record.content?.newsItem;
if (newsList) {
newsList.forEach((item: any) => {
item.picUrl = item.thumbUrl || item.picUrl;
});
}
});
// TODO @jaweArticle
return {
list: res.list as unknown as Article[],
total: res.total,
};
},
},
},
rowConfig: {
keyField: 'articleId',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<Article>,
});
</script> </script>
<template> <template>
<Page> <Page auto-content-height>
<DocAlert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" /> <template #doc>
<Button <DocAlert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
danger </template>
type="link" <Grid table-title="">
target="_blank" <template #form-accountId>
href="https://github.com/yudaocode/yudao-ui-admin-vue3" <WxAccountSelect @change="handleAccountChange" />
> </template>
该功能支持 Vue3 + element-plus 版本 <template #cover="{ row }">
</Button> <div
<br /> v-if="row.content?.newsItem && row.content.newsItem.length > 0"
<Button class="flex flex-col items-center justify-center gap-1"
type="link" >
target="_blank" <Image
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mp/freePublish/index" v-for="(item, index) in row.content.newsItem"
> :key="index"
可参考 :src="item.picUrl || item.thumbUrl"
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mp/freePublish/index class="h-36 !w-[300px] rounded object-cover"
代码pull request 贡献给我们 :alt="`文章 ${index + 1} 封面图`"
</Button> />
</div>
<span v-else class="text-gray-400">-</span>
</template>
<template #title="{ row }">
<div
v-if="row.content?.newsItem && row.content.newsItem.length > 0"
class="space-y-1"
>
<div
v-for="(item, index) in row.content.newsItem"
:key="index"
class="flex h-36 items-center justify-center"
>
<Typography.Link :href="(item as any).url" target="_blank">
{{ item.title }}
</Typography.Link>
</div>
</div>
<span v-else class="text-gray-400">-</span>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '删除',
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['mp:free-publish:delete'],
popConfirm: {
title: '是否确认删除此数据?',
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page> </Page>
</template> </template>
<style lang="scss" scoped>
:deep(.vxe-table--body-wrapper) {
.vxe-table--body {
.vxe-body--column {
.vxe-cell {
height: auto !important;
padding: 0;
}
}
}
}
</style>

View File

@ -134,20 +134,20 @@ function menuListToFrontend(list: any[]) {
list.forEach((item: RawMenu) => { list.forEach((item: RawMenu) => {
const menu: any = { const menu: any = {
...item, ...item,
}; reply: {
menu.reply = { type: item.replyMessageType,
type: item.replyMessageType, accountId: item.accountId,
accountId: item.accountId, content: item.replyContent,
content: item.replyContent, mediaId: item.replyMediaId,
mediaId: item.replyMediaId, url: item.replyMediaUrl,
url: item.replyMediaUrl, title: item.replyTitle,
title: item.replyTitle, description: item.replyDescription,
description: item.replyDescription, thumbMediaId: item.replyThumbMediaId,
thumbMediaId: item.replyThumbMediaId, thumbMediaUrl: item.replyThumbMediaUrl,
thumbMediaUrl: item.replyThumbMediaUrl, articles: item.replyArticles,
articles: item.replyArticles, musicUrl: item.replyMusicUrl,
musicUrl: item.replyMusicUrl, hqMusicUrl: item.replyHqMusicUrl,
hqMusicUrl: item.replyHqMusicUrl, },
}; };
result.push(menu as RawMenu); result.push(menu as RawMenu);
}); });
@ -275,23 +275,22 @@ function menuListToBackend() {
/** 将前端的 menu转换成后端接收的 menu */ /** 将前端的 menu转换成后端接收的 menu */
// TODO: @ API // TODO: @ API
function menuToBackend(menu: any) { function menuToBackend(menu: any) {
const result = { return {
...menu, ...menu,
children: undefined, // children: undefined, //
reply: undefined, // reply: undefined, //
replyMessageType: menu.reply.type,
replyContent: menu.reply.content,
replyMediaId: menu.reply.mediaId,
replyMediaUrl: menu.reply.url,
replyTitle: menu.reply.title,
replyDescription: menu.reply.description,
replyThumbMediaId: menu.reply.thumbMediaId,
replyThumbMediaUrl: menu.reply.thumbMediaUrl,
replyArticles: menu.reply.articles,
replyMusicUrl: menu.reply.musicUrl,
replyHqMusicUrl: menu.reply.hqMusicUrl,
}; };
result.replyMessageType = menu.reply.type;
result.replyContent = menu.reply.content;
result.replyMediaId = menu.reply.mediaId;
result.replyMediaUrl = menu.reply.url;
result.replyTitle = menu.reply.title;
result.replyDescription = menu.reply.description;
result.replyThumbMediaId = menu.reply.thumbMediaId;
result.replyThumbMediaUrl = menu.reply.thumbMediaUrl;
result.replyArticles = menu.reply.articles;
result.replyMusicUrl = menu.reply.musicUrl;
result.replyHqMusicUrl = menu.reply.hqMusicUrl;
return result;
} }
</script> </script>

View File

@ -258,9 +258,7 @@ export function useAppFormSchema(): VbenFormSchema[] {
/** 渠道新增/修改的表单 */ /** 渠道新增/修改的表单 */
export function useChannelFormSchema(formType: string = ''): VbenFormSchema[] { export function useChannelFormSchema(formType: string = ''): VbenFormSchema[] {
const schema: VbenFormSchema[] = []; const schema: VbenFormSchema[] = [
// 添加通用字段
schema.push(
{ {
component: 'Input', component: 'Input',
fieldName: 'id', fieldName: 'id',
@ -309,7 +307,8 @@ export function useChannelFormSchema(formType: string = ''): VbenFormSchema[] {
optionType: 'button', optionType: 'button',
}, },
}, },
); ];
// 添加通用字段
// 根据类型添加特定字段 // 根据类型添加特定字段
if (formType.includes('alipay_')) { if (formType.includes('alipay_')) {
schema.push( schema.push(

View File

@ -4,7 +4,8 @@
"register": "Register", "register": "Register",
"codeLogin": "Code Login", "codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login", "qrcodeLogin": "Qr Code Login",
"forgetPassword": "Forget Password" "forgetPassword": "Forget Password",
"profile": "Profile"
}, },
"dashboard": { "dashboard": {
"title": "Dashboard", "title": "Dashboard",

View File

@ -4,7 +4,8 @@
"register": "注册", "register": "注册",
"codeLogin": "验证码登录", "codeLogin": "验证码登录",
"qrcodeLogin": "二维码登录", "qrcodeLogin": "二维码登录",
"forgetPassword": "忘记密码" "forgetPassword": "忘记密码",
"profile": "个人中心"
}, },
"dashboard": { "dashboard": {
"title": "概览", "title": "概览",

View File

@ -90,8 +90,7 @@ const routes: RouteRecordRaw[] = [
title: '客户统计', title: '客户统计',
activePath: '/crm/statistics/customer', activePath: '/crm/statistics/customer',
}, },
component: () => component: () => import('#/views/crm/statistics/customer/index.vue'),
import('#/views/crm/statistics/customer/index.vue'),
}, },
{ {
path: 'statistics/funnel', path: 'statistics/funnel',
@ -109,8 +108,7 @@ const routes: RouteRecordRaw[] = [
title: '员工业绩', title: '员工业绩',
activePath: '/crm/statistics/performance', activePath: '/crm/statistics/performance',
}, },
component: () => component: () => import('#/views/crm/statistics/performance/index.vue'),
import('#/views/crm/statistics/performance/index.vue'),
}, },
{ {
path: 'statistics/portrait', path: 'statistics/portrait',
@ -119,8 +117,7 @@ const routes: RouteRecordRaw[] = [
title: '客户画像', title: '客户画像',
activePath: '/crm/statistics/portrait', activePath: '/crm/statistics/portrait',
}, },
component: () => component: () => import('#/views/crm/statistics/portrait/index.vue'),
import('#/views/crm/statistics/portrait/index.vue'),
}, },
{ {
path: 'statistics/rank', path: 'statistics/rank',

View File

@ -80,7 +80,7 @@ export const useMallKefuStore = defineStore('mall-kefu', {
}, },
conversationSort() { conversationSort() {
// 按置顶属性和最后消息时间排序 // 按置顶属性和最后消息时间排序
this.conversationList.sort((a, b) => { this.conversationList.toSorted((a, b) => {
// 按照置顶排序,置顶的会在前面 // 按照置顶排序,置顶的会在前面
if (a.adminPinned !== b.adminPinned) { if (a.adminPinned !== b.adminPinned) {
return a.adminPinned ? -1 : 1; return a.adminPinned ? -1 : 1;

View File

@ -0,0 +1,65 @@
<script setup lang="ts">
import type { BasicOption } from '@vben/types';
import type { VbenFormSchema } from '#/adapter/form';
import { computed, onMounted, ref } from 'vue';
import { ProfileBaseSetting } from '@vben/common-ui';
import { getUserInfoApi } from '#/api';
const profileBaseSettingRef = ref();
const MOCK_ROLES_OPTIONS: BasicOption[] = [
{
label: '管理员',
value: 'super',
},
{
label: '用户',
value: 'user',
},
{
label: '测试',
value: 'test',
},
];
const formSchema = computed((): VbenFormSchema[] => {
return [
{
fieldName: 'realName',
component: 'Input',
label: '姓名',
},
{
fieldName: 'username',
component: 'Input',
label: '用户名',
},
{
fieldName: 'roles',
component: 'Select',
componentProps: {
mode: 'tags',
options: MOCK_ROLES_OPTIONS,
},
label: '角色',
},
{
fieldName: 'introduction',
component: 'Textarea',
label: '个人简介',
},
];
});
onMounted(async () => {
const data = await getUserInfoApi();
profileBaseSettingRef.value.getFormApi().setValues(data);
});
</script>
<template>
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" />
</template>

View File

@ -0,0 +1,31 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileNotificationSetting } from '@vben/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '其他用户的消息将以站内信的形式通知',
},
{
value: true,
fieldName: 'systemMessage',
label: '系统消息',
description: '系统消息将以站内信的形式通知',
},
{
value: true,
fieldName: 'todoTask',
label: '待办任务',
description: '待办任务将以站内信的形式通知',
},
];
});
</script>
<template>
<ProfileNotificationSetting :form-schema="formSchema" />
</template>

View File

@ -0,0 +1,66 @@
<script setup lang="ts">
import type { VbenFormSchema } from '#/adapter/form';
import { computed, ref } from 'vue';
import { ProfilePasswordSetting, z } from '@vben/common-ui';
import { ElMessage } from 'element-plus';
const profilePasswordSettingRef = ref();
const formSchema = computed((): VbenFormSchema[] => {
return [
{
fieldName: 'oldPassword',
label: '旧密码',
component: 'VbenInputPassword',
componentProps: {
placeholder: '请输入旧密码',
},
},
{
fieldName: 'newPassword',
label: '新密码',
component: 'VbenInputPassword',
componentProps: {
passwordStrength: true,
placeholder: '请输入新密码',
},
},
{
fieldName: 'confirmPassword',
label: '确认密码',
component: 'VbenInputPassword',
componentProps: {
passwordStrength: true,
placeholder: '请再次输入新密码',
},
dependencies: {
rules(values) {
const { newPassword } = values;
return z
.string({ required_error: '请再次输入新密码' })
.min(1, { message: '请再次输入新密码' })
.refine((value) => value === newPassword, {
message: '两次输入的密码不一致',
});
},
triggerFields: ['newPassword'],
},
},
];
});
function handleSubmit() {
ElMessage.success('密码修改成功');
}
</script>
<template>
<ProfilePasswordSetting
ref="profilePasswordSettingRef"
class="w-1/3"
:form-schema="formSchema"
@submit="handleSubmit"
/>
</template>

View File

@ -0,0 +1,43 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileSecuritySetting } from '@vben/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '当前密码强度:强',
},
{
value: true,
fieldName: 'securityPhone',
label: '密保手机',
description: '已绑定手机138****8293',
},
{
value: true,
fieldName: 'securityQuestion',
label: '密保问题',
description: '未设置密保问题,密保问题可有效保护账户安全',
},
{
value: true,
fieldName: 'securityEmail',
label: '备用邮箱',
description: '已绑定邮箱ant***sign.com',
},
{
value: false,
fieldName: 'securityMfa',
label: 'MFA 设备',
description: '未绑定 MFA 设备,绑定后,可以进行二次确认',
},
];
});
</script>
<template>
<ProfileSecuritySetting :form-schema="formSchema" />
</template>

View File

@ -411,8 +411,8 @@ async function doSendMessageStream(userMessage: AiChatMessageApi.ChatMessage) {
const lastMessage = const lastMessage =
activeMessageList.value[activeMessageList.value.length - 1]; activeMessageList.value[activeMessageList.value.length - 1];
// //
lastMessage.reasoningContent = lastMessage!.reasoningContent =
(lastMessage.reasoningContent || '') + (lastMessage!.reasoningContent || '') +
data.receive.reasoningContent; data.receive.reasoningContent;
} }
@ -565,7 +565,7 @@ onMounted(async () => {
<!-- 右侧详情部分 --> <!-- 右侧详情部分 -->
<ElContainer direction="vertical" class="bg-card mx-4 flex-1"> <ElContainer direction="vertical" class="bg-card mx-4 flex-1">
<ElHeader <ElHeader
class="!bg-card border-border flex !h-12 items-center justify-between border-b !px-4" class="border-border !bg-card flex !h-12 items-center justify-between border-b !px-4"
> >
<div class="text-lg font-bold"> <div class="text-lg font-bold">
{{ activeConversation?.title ? activeConversation?.title : '对话' }} {{ activeConversation?.title ? activeConversation?.title : '对话' }}

View File

@ -97,7 +97,7 @@ async function getChatConversationList() {
// 1.1 // 1.1
conversationList.value = await getChatConversationMyList(); conversationList.value = await getChatConversationMyList();
// 1.2 // 1.2
conversationList.value.sort((a, b) => { conversationList.value.toSorted((a, b) => {
return Number(b.createTime) - Number(a.createTime); return Number(b.createTime) - Number(a.createTime);
}); });
// 1.3 // 1.3

View File

@ -138,14 +138,14 @@ async function uploadFile(fileItem: FileItem) {
fileItem.progress = 100; fileItem.progress = 100;
// //
console.log('上传响应:', response); console.warn('上传响应:', response);
// { url: '...' } { data: '...' } // { url: '...' } { data: '...' }
const fileUrl = const fileUrl =
(response as any)?.url || (response as any)?.data || response; (response as any)?.url || (response as any)?.data || response;
fileItem.url = fileUrl; fileItem.url = fileUrl;
console.log('提取的文件 URL:', fileUrl); console.warn('提取的文件 URL:', fileUrl);
// URL // URL
if (fileUrl && typeof fileUrl === 'string') { if (fileUrl && typeof fileUrl === 'string') {

View File

@ -144,7 +144,7 @@ async function handleImageMidjourneyButtonClick(
const data = { const data = {
id: imageDetail.id, id: imageDetail.id,
customId: button.customId, customId: button.customId,
} as AiImageApi.ImageMidjourneyActionVO; } as AiImageApi.ImageMidjourneyAction;
// 2. action // 2. action
await midjourneyAction(data); await midjourneyAction(data);
// 3. // 3.

View File

@ -13,7 +13,6 @@ import {
getChatRole, getChatRole,
updateChatRole, updateChatRole,
} from '#/api/ai/model/chatRole'; } from '#/api/ai/model/chatRole';
import {} from '#/api/bpm/model';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -9,7 +9,6 @@ import { ElMessage } from 'element-plus';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { createModel, getModel, updateModel } from '#/api/ai/model/model'; import { createModel, getModel, updateModel } from '#/api/ai/model/model';
import {} from '#/api/bpm/model';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -17,7 +17,7 @@ const currentSong = ref({}); // 当前音乐
const mySongList = ref<Recordable<any>[]>([]); const mySongList = ref<Recordable<any>[]>([]);
const squareSongList = ref<Recordable<any>[]>([]); const squareSongList = ref<Recordable<any>[]>([]);
function generateMusic(formData: Recordable<any>) { function generateMusic(_formData: Recordable<any>) {
loading.value = true; loading.value = true;
setTimeout(() => { setTimeout(() => {
mySongList.value = Array.from({ length: 20 }, (_, index) => { mySongList.value = Array.from({ length: 20 }, (_, index) => {

View File

@ -21,7 +21,7 @@ const emits = defineEmits<{
<span <span
v-for="tag in props.tags" v-for="tag in props.tags"
:key="tag.value" :key="tag.value"
class="bg-card border-card-100 mb-2 cursor-pointer rounded border-2 border-solid px-1 text-xs leading-6" class="border-card-100 bg-card mb-2 cursor-pointer rounded border-2 border-solid px-1 text-xs leading-6"
:class=" :class="
modelValue === tag.value && '!border-primary-500 !text-primary-500' modelValue === tag.value && '!border-primary-500 !text-primary-500'
" "

View File

@ -6,7 +6,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -54,7 +54,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -102,7 +102,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -150,7 +150,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -198,7 +198,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -246,7 +246,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -294,7 +294,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,
@ -342,7 +342,7 @@ export function getChartOptions(activeTabName: any, res: any): any {
return { return {
dataset: { dataset: {
dimensions: ['nickname', 'count'], dimensions: ['nickname', 'count'],
source: cloneDeep(res).reverse(), source: cloneDeep(res).toReversed(),
}, },
grid: { grid: {
left: 20, left: 20,

View File

@ -24,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 }, { name: '定制', value: 310 },
{ name: '技术支持', value: 274 }, { name: '技术支持', value: 274 },
{ name: '远程', value: 400 }, { name: '远程', value: 400 },
].sort((a, b) => { ].toSorted((a, b) => {
return a.value - b.value; return a.value - b.value;
}), }),
name: '商业占比', name: '商业占比',

View File

@ -128,6 +128,7 @@ const handleOpenPurchaseIn = () => {
const handleAddPurchaseIn = (rows: ErpPurchaseInApi.PurchaseIn[]) => { const handleAddPurchaseIn = (rows: ErpPurchaseInApi.PurchaseIn[]) => {
rows.forEach((row) => { rows.forEach((row) => {
// TODO @
const newItem: ErpFinancePaymentApi.FinancePaymentItem = { const newItem: ErpFinancePaymentApi.FinancePaymentItem = {
bizId: row.id, bizId: row.id,
bizType: ErpBizType.PURCHASE_IN, bizType: ErpBizType.PURCHASE_IN,

View File

@ -127,6 +127,7 @@ function handleOpenSaleOut() {
} }
function handleAddSaleOut(rows: ErpSaleOutApi.SaleOut[]) { function handleAddSaleOut(rows: ErpSaleOutApi.SaleOut[]) {
// TODO @
rows.forEach((row) => { rows.forEach((row) => {
const newItem: ErpFinanceReceiptApi.FinanceReceiptItem = { const newItem: ErpFinanceReceiptApi.FinanceReceiptItem = {
bizId: row.id, bizId: row.id,

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpPurchaseInApi.PurchaseInItem) { function handleDelete(row: ErpPurchaseInApi.PurchaseInItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -142,6 +142,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrderItem) { function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrderItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);
@ -169,6 +170,7 @@ async function handleProductChange(productId: any, row: any) {
/** 处理行数据变更 */ /** 处理行数据变更 */
function handleRowChange(row: any) { function handleRowChange(row: any) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index === -1) { if (index === -1) {
tableData.value.push(row); tableData.value.push(row);

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpPurchaseReturnApi.PurchaseReturnItem) { function handleDelete(row: ErpPurchaseReturnApi.PurchaseReturnItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -142,6 +142,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpSaleOrderApi.SaleOrderItem) { function handleDelete(row: ErpSaleOrderApi.SaleOrderItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpSaleOutApi.SaleOutItem) { function handleDelete(row: ErpSaleOutApi.SaleOutItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -131,6 +131,7 @@ watch(
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpSaleReturnApi.SaleReturnItem) { function handleDelete(row: ErpSaleReturnApi.SaleReturnItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -106,6 +106,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpStockCheckApi.StockCheckItem) { function handleDelete(row: ErpStockCheckApi.StockCheckItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -86,6 +86,7 @@ watch(
/** 处理新增 */ /** 处理新增 */
function handleAdd() { function handleAdd() {
// TODO @
const newRow = { const newRow = {
id: undefined, id: undefined,
warehouseId: undefined, warehouseId: undefined,

View File

@ -105,6 +105,7 @@ function handleAdd() {
/** 处理删除 */ /** 处理删除 */
function handleDelete(row: ErpStockOutApi.StockOutItem) { function handleDelete(row: ErpStockOutApi.StockOutItem) {
// TODO @
const index = tableData.value.findIndex((item) => item.seq === row.seq); const index = tableData.value.findIndex((item) => item.seq === row.seq);
if (index !== -1) { if (index !== -1) {
tableData.value.splice(index, 1); tableData.value.splice(index, 1);

View File

@ -47,7 +47,7 @@ const { status, data, send, close, open } = useWebSocket(server.value, {
const messageList = ref( const messageList = ref(
[] as { text: string; time: number; type?: string; userId?: string }[], [] as { text: string; time: number; type?: string; userId?: string }[],
); // ); //
const messageReverseList = computed(() => [...messageList.value].reverse()); const messageReverseList = computed(() => [...messageList.value].toReversed());
watchEffect(() => { watchEffect(() => {
if (!data.value) { if (!data.value) {
return; return;

View File

@ -111,6 +111,7 @@ function emitActivityChange() {
> >
<ElTooltip :content="activity.name"> <ElTooltip :content="activity.name">
<div class="relative h-full w-full"> <div class="relative h-full w-full">
<!-- TODO @芋艿 -->
<ElImage <ElImage
:src="activity.picUrl" :src="activity.picUrl"
class="h-full w-full rounded-lg object-cover" class="h-full w-full rounded-lg object-cover"

View File

@ -57,10 +57,10 @@ export function isContains(hotArea: Rect, point: Point): boolean {
*/ */
export function createRect(a: Point, b: Point): Rect { export function createRect(a: Point, b: Point): Rect {
// 计算矩形的范围 // 计算矩形的范围
let [left, left2] = [a.x, b.x].sort(); let [left, left2] = [a.x, b.x].toSorted();
left = left ?? 0; left = left ?? 0;
left2 = left2 ?? 0; left2 = left2 ?? 0;
let [top, top2] = [a.y, b.y].sort(); let [top, top2] = [a.y, b.y].toSorted();
top = top ?? 0; top = top ?? 0;
top2 = top2 ?? 0; top2 = top2 ?? 0;
const right = left2 + 1; const right = left2 + 1;

View File

@ -97,7 +97,7 @@ function pushMessage(message: any) {
/** 按照时间倒序,获取消息列表 */ /** 按照时间倒序,获取消息列表 */
const getMessageList0 = computed(() => { const getMessageList0 = computed(() => {
// 使 // 使
return [...messageList.value].sort( return [...messageList.value].toSorted(
(a: any, b: any) => a.createTime - b.createTime, (a: any, b: any) => a.createTime - b.createTime,
); );
}); });

View File

@ -110,6 +110,7 @@ function emitActivityChange() {
> >
<ElTooltip :content="activity.name"> <ElTooltip :content="activity.name">
<div class="relative h-full w-full"> <div class="relative h-full w-full">
<!-- TODO @芋艿 -->
<ElImage <ElImage
:src="activity.picUrl" :src="activity.picUrl"
class="h-full w-full rounded-lg object-cover" class="h-full w-full rounded-lg object-cover"

View File

@ -111,20 +111,22 @@ const [Modal, modalApi] = useVbenModal({
if (data.row?.id) { if (data.row?.id) {
// //
const rowData = data.row; const rowData = data.row;
const formValues: any = { ...rowData }; const formValues: any = {
formValues.reply = { ...rowData,
type: rowData.responseMessageType, reply: {
accountId: data.accountId || -1, type: rowData.responseMessageType,
content: rowData.responseContent, accountId: data.accountId || -1,
mediaId: rowData.responseMediaId, content: rowData.responseContent,
url: rowData.responseMediaUrl, mediaId: rowData.responseMediaId,
title: rowData.responseTitle, url: rowData.responseMediaUrl,
description: rowData.responseDescription, title: rowData.responseTitle,
thumbMediaId: rowData.responseThumbMediaId, description: rowData.responseDescription,
thumbMediaUrl: rowData.responseThumbMediaUrl, thumbMediaId: rowData.responseThumbMediaId,
articles: rowData.responseArticles, thumbMediaUrl: rowData.responseThumbMediaUrl,
musicUrl: rowData.responseMusicUrl, articles: rowData.responseArticles,
hqMusicUrl: rowData.responseHqMusicUrl, musicUrl: rowData.responseMusicUrl,
hqMusicUrl: rowData.responseHqMusicUrl,
},
}; };
await formApi.setValues(formValues); await formApi.setValues(formValues);
} else { } else {

View File

@ -106,7 +106,7 @@ async function getPage(page: any, params: any = null) {
const scrollHeight = msgDivRef.value?.scrollHeight ?? 0; const scrollHeight = msgDivRef.value?.scrollHeight ?? 0;
// //
const data = dataTemp.list.reverse(); const data = dataTemp.list.toReversed();
list.value = [...data, ...list.value]; list.value = [...data, ...list.value];
loading.value = false; loading.value = false;
if (data.length < queryParams.pageSize || data.length === 0) { if (data.length < queryParams.pageSize || data.length === 0) {

View File

@ -72,11 +72,11 @@ async function handleDelete(row: MpDraftApi.DraftArticle) {
return; return;
} }
const hideLoading = ElLoading.service({ const hideLoading = ElLoading.service({
text: '删除中...', text: $t('ui.actionMessage.deleting'),
}); });
try { try {
await deleteDraft(accountId, row.mediaId); await deleteDraft(accountId, row.mediaId);
ElMessage.success('删除成功'); ElMessage.success($t('ui.actionMessage.deleteSuccess'));
handleRefresh(); handleRefresh();
} finally { } finally {
hideLoading.close(); hideLoading.close();

View File

@ -0,0 +1,59 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
import type { MpAccountApi } from '#/api/mp/account';
import { formatDateTime } from '@vben/utils';
import { getSimpleAccountList } from '#/api/mp/account';
let accountList: MpAccountApi.AccountSimple[] = [];
getSimpleAccountList().then((data) => (accountList = data));
/** 搜索表单配置 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'accountId',
label: '公众号',
component: 'Select',
componentProps: {
options: accountList.map((item) => ({
label: item.name,
value: item.id,
})),
placeholder: '请选择公众号',
clearable: true,
},
},
];
}
/** 表格列配置 */
export function useGridColumns(): VxeGridPropTypes.Columns {
return [
{
field: 'cover',
title: '图片',
width: 360,
slots: { default: 'cover' },
},
{
field: 'title',
title: '标题',
slots: { default: 'title' },
},
{
field: 'updateTime',
title: '修改时间',
formatter: ({ row }) => {
return formatDateTime(row.updateTime * 1000);
},
},
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

Some files were not shown because too many files have changed in this diff Show More