Browse Source

图标修改

master
zhanglei 1 month ago
parent
commit
84f2c86492
  1. 2
      templates/vue/api.ts.j2
  2. 31
      templates/vue/index.vue.j2
  3. 86
      vue-vben-admin/apps/web-antd/mock/Health_device.ts
  4. 69
      vue-vben-admin/apps/web-antd/src/api/device.ts
  5. 31
      vue-vben-admin/apps/web-antd/src/router/routes/modules/device.ts
  6. 352
      vue-vben-admin/apps/web-antd/src/views/device/data.ts
  7. 384
      vue-vben-admin/apps/web-antd/src/views/device/form.ts
  8. 684
      vue-vben-admin/apps/web-antd/src/views/device/index.vue

2
templates/vue/api.ts.j2

@ -62,7 +62,7 @@ export namespace {{entity}}Api {
* 上传图片 * 上传图片
*/ */
export function upload(params: any) { export function upload(params: any) {
return requestClient.post(applicationConfig.javaURL+'/file/upImg', params, return requestClient.post(applicationConfig.javaURL+'/file/up', params,
{ headers: {'Content-Type': 'multipart/form-data', Token: useAccessStore().accessToken, version: '1.0.1'}}); { headers: {'Content-Type': 'multipart/form-data', Token: useAccessStore().accessToken, version: '1.0.1'}});
} }

31
templates/vue/index.vue.j2

@ -140,10 +140,14 @@ function getFullUrl(url: string) {
const gridOptions = { const gridOptions = {
columns: [ columns: [
...columns.map(col => { ...columns.map(col => {
const baseCol = {
...col,
fixed: col.fixed ?? null,
};
// ✅ 如果是图片字段 (包含 img, face, picture),使用图片渲染 + 上传按钮 // ✅ 如果是图片字段 (包含 img, face, picture),使用图片渲染 + 上传按钮
if (isImageField(col.field)) { if (isImageField(col.field)) {
return { return {
...col, ...baseCol,
width: 200, width: 200,
align: 'center', align: 'center',
slots: { slots: {
@ -174,7 +178,7 @@ const gridOptions = {
} }
}; };
} }
return col; return baseCol;
}), }),
{ {
field: 'action', field: 'action',
@ -184,6 +188,10 @@ const gridOptions = {
slots: {default: 'action'}, slots: {default: 'action'},
}, },
], ],
customConfig: {
storage: true, // 已有功能:列显示/隐藏缓存
allowFixed: true, // 🔥 开启“冻结列”功能(关键)
},
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({page}) => { query: async ({page}) => {
@ -564,6 +572,12 @@ function handleReset() {
queryFormApi.resetForm(); queryFormApi.resetForm();
gridApi.reload(); gridApi.reload();
} }
// ========== 列设置 ==========
function openColumnSetting() {
const $grid = gridApi?.grid || gridApi?.getVxeGrid?.();
$grid?.openCustom();
}
</script> </script>
@ -593,8 +607,17 @@ function handleReset() {
<div class="flex-1 overflow-hidden bg-card rounded shadow"> <div class="flex-1 overflow-hidden bg-card rounded shadow">
<Grid> <Grid>
<template #toolbar-tools> <template #toolbar-tools>
<button @click="handleAdd" class="mr-2">➕ 新增</button> <button @click="handleAdd" class="vxe-button type--button size--small is--circle" title="新增">
<button @click="() => gridApi.reload()">🔄 刷新</button> <svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path fill="currentColor" d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"></path>
</svg>
</button>
<button @click="openColumnSetting" class="vxe-button type--button vxe-toolbar-custom-target size--small is--circle" title="列设置">
<i class="vxe-button--item vxe-button--prefix-icon vxe-table-icon-custom-column"></i>
</button>
<button @click="() => gridApi.reload()" class="vxe-button type--button size--small is--circle" title="刷新" type="button">
<i class="vxe-button--item vxe-button--prefix-icon vxe-table-icon-repeat"></i>
</button>
</template> </template>
<template #action="{ row }"> <template #action="{ row }">

86
vue-vben-admin/apps/web-antd/mock/Health_device.ts

@ -39,6 +39,8 @@ const list = [
deletedFlag: 'deletedFlag_0', deletedFlag: 'deletedFlag_0',
userId: 'userId_0',
classId: 'classId_0', classId: 'classId_0',
deviceUrl: 'deviceUrl_0', deviceUrl: 'deviceUrl_0',
@ -55,11 +57,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_0', deviceAddressIp: 'deviceAddressIp_0',
deviceBind: 'deviceBind_0',
deviceImg: 'deviceImg_0', deviceImg: 'deviceImg_0',
warnImg: 'warnImg_0', warnImg: 'warnImg_0',
companyId: 'companyId_0', familyId: 'familyId_0',
serverVersion: 'serverVersion_0', serverVersion: 'serverVersion_0',
@ -67,8 +71,6 @@ const list = [
upSwitch: 'upSwitch_0', upSwitch: 'upSwitch_0',
userId: 'userId_0',
}, },
{ {
@ -103,6 +105,8 @@ const list = [
deletedFlag: 'deletedFlag_1', deletedFlag: 'deletedFlag_1',
userId: 'userId_1',
classId: 'classId_1', classId: 'classId_1',
deviceUrl: 'deviceUrl_1', deviceUrl: 'deviceUrl_1',
@ -119,11 +123,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_1', deviceAddressIp: 'deviceAddressIp_1',
deviceBind: 'deviceBind_1',
deviceImg: 'deviceImg_1', deviceImg: 'deviceImg_1',
warnImg: 'warnImg_1', warnImg: 'warnImg_1',
companyId: 'companyId_1', familyId: 'familyId_1',
serverVersion: 'serverVersion_1', serverVersion: 'serverVersion_1',
@ -131,8 +137,6 @@ const list = [
upSwitch: 'upSwitch_1', upSwitch: 'upSwitch_1',
userId: 'userId_1',
}, },
{ {
@ -167,6 +171,8 @@ const list = [
deletedFlag: 'deletedFlag_2', deletedFlag: 'deletedFlag_2',
userId: 'userId_2',
classId: 'classId_2', classId: 'classId_2',
deviceUrl: 'deviceUrl_2', deviceUrl: 'deviceUrl_2',
@ -183,11 +189,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_2', deviceAddressIp: 'deviceAddressIp_2',
deviceBind: 'deviceBind_2',
deviceImg: 'deviceImg_2', deviceImg: 'deviceImg_2',
warnImg: 'warnImg_2', warnImg: 'warnImg_2',
companyId: 'companyId_2', familyId: 'familyId_2',
serverVersion: 'serverVersion_2', serverVersion: 'serverVersion_2',
@ -195,8 +203,6 @@ const list = [
upSwitch: 'upSwitch_2', upSwitch: 'upSwitch_2',
userId: 'userId_2',
}, },
{ {
@ -231,6 +237,8 @@ const list = [
deletedFlag: 'deletedFlag_3', deletedFlag: 'deletedFlag_3',
userId: 'userId_3',
classId: 'classId_3', classId: 'classId_3',
deviceUrl: 'deviceUrl_3', deviceUrl: 'deviceUrl_3',
@ -247,11 +255,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_3', deviceAddressIp: 'deviceAddressIp_3',
deviceBind: 'deviceBind_3',
deviceImg: 'deviceImg_3', deviceImg: 'deviceImg_3',
warnImg: 'warnImg_3', warnImg: 'warnImg_3',
companyId: 'companyId_3', familyId: 'familyId_3',
serverVersion: 'serverVersion_3', serverVersion: 'serverVersion_3',
@ -259,8 +269,6 @@ const list = [
upSwitch: 'upSwitch_3', upSwitch: 'upSwitch_3',
userId: 'userId_3',
}, },
{ {
@ -295,6 +303,8 @@ const list = [
deletedFlag: 'deletedFlag_4', deletedFlag: 'deletedFlag_4',
userId: 'userId_4',
classId: 'classId_4', classId: 'classId_4',
deviceUrl: 'deviceUrl_4', deviceUrl: 'deviceUrl_4',
@ -311,11 +321,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_4', deviceAddressIp: 'deviceAddressIp_4',
deviceBind: 'deviceBind_4',
deviceImg: 'deviceImg_4', deviceImg: 'deviceImg_4',
warnImg: 'warnImg_4', warnImg: 'warnImg_4',
companyId: 'companyId_4', familyId: 'familyId_4',
serverVersion: 'serverVersion_4', serverVersion: 'serverVersion_4',
@ -323,8 +335,6 @@ const list = [
upSwitch: 'upSwitch_4', upSwitch: 'upSwitch_4',
userId: 'userId_4',
}, },
{ {
@ -359,6 +369,8 @@ const list = [
deletedFlag: 'deletedFlag_5', deletedFlag: 'deletedFlag_5',
userId: 'userId_5',
classId: 'classId_5', classId: 'classId_5',
deviceUrl: 'deviceUrl_5', deviceUrl: 'deviceUrl_5',
@ -375,11 +387,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_5', deviceAddressIp: 'deviceAddressIp_5',
deviceBind: 'deviceBind_5',
deviceImg: 'deviceImg_5', deviceImg: 'deviceImg_5',
warnImg: 'warnImg_5', warnImg: 'warnImg_5',
companyId: 'companyId_5', familyId: 'familyId_5',
serverVersion: 'serverVersion_5', serverVersion: 'serverVersion_5',
@ -387,8 +401,6 @@ const list = [
upSwitch: 'upSwitch_5', upSwitch: 'upSwitch_5',
userId: 'userId_5',
}, },
{ {
@ -423,6 +435,8 @@ const list = [
deletedFlag: 'deletedFlag_6', deletedFlag: 'deletedFlag_6',
userId: 'userId_6',
classId: 'classId_6', classId: 'classId_6',
deviceUrl: 'deviceUrl_6', deviceUrl: 'deviceUrl_6',
@ -439,11 +453,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_6', deviceAddressIp: 'deviceAddressIp_6',
deviceBind: 'deviceBind_6',
deviceImg: 'deviceImg_6', deviceImg: 'deviceImg_6',
warnImg: 'warnImg_6', warnImg: 'warnImg_6',
companyId: 'companyId_6', familyId: 'familyId_6',
serverVersion: 'serverVersion_6', serverVersion: 'serverVersion_6',
@ -451,8 +467,6 @@ const list = [
upSwitch: 'upSwitch_6', upSwitch: 'upSwitch_6',
userId: 'userId_6',
}, },
{ {
@ -487,6 +501,8 @@ const list = [
deletedFlag: 'deletedFlag_7', deletedFlag: 'deletedFlag_7',
userId: 'userId_7',
classId: 'classId_7', classId: 'classId_7',
deviceUrl: 'deviceUrl_7', deviceUrl: 'deviceUrl_7',
@ -503,11 +519,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_7', deviceAddressIp: 'deviceAddressIp_7',
deviceBind: 'deviceBind_7',
deviceImg: 'deviceImg_7', deviceImg: 'deviceImg_7',
warnImg: 'warnImg_7', warnImg: 'warnImg_7',
companyId: 'companyId_7', familyId: 'familyId_7',
serverVersion: 'serverVersion_7', serverVersion: 'serverVersion_7',
@ -515,8 +533,6 @@ const list = [
upSwitch: 'upSwitch_7', upSwitch: 'upSwitch_7',
userId: 'userId_7',
}, },
{ {
@ -551,6 +567,8 @@ const list = [
deletedFlag: 'deletedFlag_8', deletedFlag: 'deletedFlag_8',
userId: 'userId_8',
classId: 'classId_8', classId: 'classId_8',
deviceUrl: 'deviceUrl_8', deviceUrl: 'deviceUrl_8',
@ -567,11 +585,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_8', deviceAddressIp: 'deviceAddressIp_8',
deviceBind: 'deviceBind_8',
deviceImg: 'deviceImg_8', deviceImg: 'deviceImg_8',
warnImg: 'warnImg_8', warnImg: 'warnImg_8',
companyId: 'companyId_8', familyId: 'familyId_8',
serverVersion: 'serverVersion_8', serverVersion: 'serverVersion_8',
@ -579,8 +599,6 @@ const list = [
upSwitch: 'upSwitch_8', upSwitch: 'upSwitch_8',
userId: 'userId_8',
}, },
{ {
@ -615,6 +633,8 @@ const list = [
deletedFlag: 'deletedFlag_9', deletedFlag: 'deletedFlag_9',
userId: 'userId_9',
classId: 'classId_9', classId: 'classId_9',
deviceUrl: 'deviceUrl_9', deviceUrl: 'deviceUrl_9',
@ -631,11 +651,13 @@ const list = [
deviceAddressIp: 'deviceAddressIp_9', deviceAddressIp: 'deviceAddressIp_9',
deviceBind: 'deviceBind_9',
deviceImg: 'deviceImg_9', deviceImg: 'deviceImg_9',
warnImg: 'warnImg_9', warnImg: 'warnImg_9',
companyId: 'companyId_9', familyId: 'familyId_9',
serverVersion: 'serverVersion_9', serverVersion: 'serverVersion_9',
@ -643,8 +665,6 @@ const list = [
upSwitch: 'upSwitch_9', upSwitch: 'upSwitch_9',
userId: 'userId_9',
}, },
@ -653,7 +673,7 @@ const list = [
export default [ export default [
{ {
url: '/api/Health_device/page', url: '/api/health_device/page',
method: 'get', method: 'get',
response: () => { response: () => {
return { return {
@ -667,7 +687,7 @@ export default [
}, },
{ {
url: '/api/Health_device', url: '/api/health_device',
method: 'post', method: 'post',
response: () => { response: () => {
return { return {
@ -678,7 +698,7 @@ export default [
}, },
{ {
url: '/api/Health_device/:id', url: '/api/health_device/:id',
method: 'delete', method: 'delete',
response: () => { response: () => {
return { return {

69
vue-vben-admin/apps/web-antd/src/api/device.ts

@ -1,69 +0,0 @@
/**
* API
*
*/
import { requestClient } from '#/api/request';
import { useAppConfig } from '@vben/hooks';
import { useAccessStore } from '@vben/stores';
export namespace deviceApi {
const applicationConfig = useAppConfig(import.meta.env, import.meta.env.PROD);
console.log('=== 接口域名 ===', applicationConfig.javaURL)
/**
*
*/
export function page(params: any) {
return requestClient.post(applicationConfig.javaURL+'/health-device/page', params,
{ headers: {'Content-Type': 'application/json', Token: useAccessStore().accessToken, version: '1.0.1'}});
}
/**
*
*/
export function get(id: number) {
return requestClient.get(applicationConfig.javaURL+'/health-device/' + id);
}
/**
*
*/
export function add(data: any) {
return requestClient.post(applicationConfig.javaURL+'/health-device/add', data,
{ headers: {'Content-Type': 'application/json', Token: useAccessStore().accessToken, version: '1.0.1'}});
}
/**
*
*/
export function save(data: any) {
return requestClient.post(applicationConfig.javaURL+'/health-device/modify', data,
{ headers: {'Content-Type': 'application/json', Token: useAccessStore().accessToken, version: '1.0.1'}});
}
/**
*
*/
export function remove(id: number) {
return requestClient.delete(applicationConfig.javaURL+'/health-device/' + id);
}
/**
*
*/
export function enumList(params: any) {
return requestClient.post(applicationConfig.javaURL+'/health-enums/optionList', params,
{ headers: {'Content-Type': 'application/json', Token: useAccessStore().accessToken, version: '1.0.1'}});
}
/**
*
*/
export function upload(params: any) {
return requestClient.post(applicationConfig.javaURL+'/file/up', params,
{ headers: {'Content-Type': 'multipart/form-data', Token: useAccessStore().accessToken, version: '1.0.1'}});
}
}

31
vue-vben-admin/apps/web-antd/src/router/routes/modules/device.ts

@ -1,31 +0,0 @@
/**
*
*/
import type { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/device',
name: '设备表模块',
meta: {
icon: 'ic:baseline-view-in-ar',
order: 1000,
keepAlive: true,
title: '',
},
children: [
{
meta: {
title: "设备表列表",
},
name: 'deviceList',
path: '/device',
component: () => import('#/views/device/index.vue'),
},
],
}
];
export default routes;

352
vue-vben-admin/apps/web-antd/src/views/device/data.ts

@ -1,352 +0,0 @@
/**
*
*/
import type { VxeGridProps } from '#/adapter/vxe-table';
export const columns: VxeGridProps['columns'] = [
{
// 列标题
title: '主键ID',
// 对应字段
field: 'id',
// 宽度
width: 150
},
{
// 列标题
title: '设备名称',
// 对应字段
field: 'deviceName',
// 宽度
width: 150
},
{
// 列标题
title: '设备品牌',
// 对应字段
field: 'deviceBrand',
// 宽度
width: 150
},
{
// 列标题
title: '设备型号',
// 对应字段
field: 'deviceModel',
// 宽度
width: 150
},
{
// 列标题
title: '设备类型:1边缘服务,2摄像头 3 热成像',
// 对应字段
field: 'deviceType',
// 宽度
width: 150
},
{
// 列标题
title: '排序',
// 对应字段
field: 'deviceOrder',
// 宽度
width: 150
},
{
// 列标题
title: '在线状态:0在线1不在线',
// 对应字段
field: 'deviceOnline',
// 宽度
width: 150
},
{
// 列标题
title: '房间号',
// 对应字段
field: 'roomId',
// 宽度
width: 150
},
{
// 列标题
title: '房间名称',
// 对应字段
field: 'roomName',
// 宽度
width: 150
},
{
// 列标题
title: '设备开关: 0 开 1关 2停用',
// 对应字段
field: 'deviceSwitch',
// 宽度
width: 150
},
{
// 列标题
title: '创建时间',
// 对应字段
field: 'createdAt',
// 宽度
width: 150
},
{
// 列标题
title: '创建人',
// 对应字段
field: 'createdBy',
// 宽度
width: 150
},
{
// 列标题
title: '更新时间',
// 对应字段
field: 'updatedAt',
// 宽度
width: 150
},
{
// 列标题
title: '更新人',
// 对应字段
field: 'updatedBy',
// 宽度
width: 150
},
{
// 列标题
title: '删除: 0 默认 1删除',
// 对应字段
field: 'deletedFlag',
// 宽度
width: 150
},
{
// 列标题
title: '用户ID',
// 对应字段
field: 'userId',
// 宽度
width: 150
},
{
// 列标题
title: '分类ID',
// 对应字段
field: 'classId',
// 宽度
width: 150
},
{
// 列标题
title: '设备URL地址',
// 对应字段
field: 'deviceUrl',
// 宽度
width: 150
},
{
// 列标题
title: '设备唯一标识',
// 对应字段
field: 'deviceCode',
// 宽度
width: 150
},
{
// 列标题
title: '设备账号',
// 对应字段
field: 'deviceLanUser',
// 宽度
width: 150
},
{
// 列标题
title: '设备密码',
// 对应字段
field: 'deviceLanPassword',
// 宽度
width: 150
},
{
// 列标题
title: '设备IP',
// 对应字段
field: 'deviceLanIp',
// 宽度
width: 150
},
{
// 列标题
title: '备注',
// 对应字段
field: 'deviceNote',
// 宽度
width: 150
},
{
// 列标题
title: '外网IP',
// 对应字段
field: 'deviceAddressIp',
// 宽度
width: 150
},
{
// 列标题
title: '是否绑定: 0未绑定,1绑定',
// 对应字段
field: 'deviceBind',
// 宽度
width: 150
},
{
// 列标题
title: '摄像头截图',
// 对应字段
field: 'deviceImg',
// 宽度
width: 150
},
{
// 列标题
title: '告警图片',
// 对应字段
field: 'warnImg',
// 宽度
width: 150
},
{
// 列标题
title: '家庭ID',
// 对应字段
field: 'familyId',
// 宽度
width: 150
},
{
// 列标题
title: '服务版本号',
// 对应字段
field: 'serverVersion',
// 宽度
width: 150
},
{
// 列标题
title: 'ai版本号',
// 对应字段
field: 'aiVersion',
// 宽度
width: 150
},
{
// 列标题
title: 'APP升级开关默认关闭',
// 对应字段
field: 'upSwitch',
// 宽度
width: 150
},
];

384
vue-vben-admin/apps/web-antd/src/views/device/form.ts

@ -1,384 +0,0 @@
/**
* schema
* component parse_component()
*/
import type { VbenFormSchema } from '#/adapter/form';
export const formSchema: VbenFormSchema[] = [
{
// 字段名
fieldName: 'id',
// label
label: '主键ID',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'deviceName',
// label
label: '设备名称',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceBrand',
// label
label: '设备品牌',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceModel',
// label
label: '设备型号',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceType',
// label
label: '设备类型:1边缘服务,2摄像头 3 热成像',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'deviceOrder',
// label
label: '排序',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'deviceOnline',
// label
label: '在线状态:0在线1不在线',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'roomId',
// label
label: '房间号',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'roomName',
// label
label: '房间名称',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceSwitch',
// label
label: '设备开关: 0 开 1关 2停用',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'createdAt',
// label
label: '创建时间',
// 自动组件
component: 'DatePicker'
},
{
// 字段名
fieldName: 'createdBy',
// label
label: '创建人',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'updatedAt',
// label
label: '更新时间',
// 自动组件
component: 'DatePicker'
},
{
// 字段名
fieldName: 'updatedBy',
// label
label: '更新人',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'deletedFlag',
// label
label: '删除: 0 默认 1删除',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'userId',
// label
label: '用户ID',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'classId',
// label
label: '分类ID',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'deviceUrl',
// label
label: '设备URL地址',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceCode',
// label
label: '设备唯一标识',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceLanUser',
// label
label: '设备账号',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceLanPassword',
// label
label: '设备密码',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceLanIp',
// label
label: '设备IP',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceNote',
// label
label: '备注',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceAddressIp',
// label
label: '外网IP',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'deviceBind',
// label
label: '是否绑定: 0未绑定,1绑定',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'deviceImg',
// label
label: '摄像头截图',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'warnImg',
// label
label: '告警图片',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'familyId',
// label
label: '家庭ID',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'serverVersion',
// label
label: '服务版本号',
// 自动组件
component: 'InputNumber'
},
{
// 字段名
fieldName: 'aiVersion',
// label
label: 'ai版本号',
// 自动组件
component: 'Input'
},
{
// 字段名
fieldName: 'upSwitch',
// label
label: 'APP升级开关默认关闭',
// 自动组件
component: 'InputNumber'
},
];

684
vue-vben-admin/apps/web-antd/src/views/device/index.vue

@ -1,684 +0,0 @@
<script setup lang="ts">
/**
* device管理页面
*/
import {ref, reactive, onMounted, watch, h} from 'vue';
import {useVbenVxeGrid} from '#/adapter/vxe-table';
import {useVbenModal} from '@vben/common-ui';
import {useVbenForm} from '#/adapter/form';
import {Upload, message, Image} from 'ant-design-vue';
import dayjs from 'dayjs';
import {columns} from './data';
import {formSchema} from './form';
import { deviceApi } from '#/api/device';
// ========== API URL ==========
const API_BASE_URL = import.meta.env.VITE_GLOB_API_URL || '';
// ========== ==========
const currentRow = ref(null);
const isEdit = ref(false);
const modalTitle = ref('新增');
const uploadFieldName = ref('');
const uploadImageUrl = ref('');
const uploadedUrl = ref(''); //
const formUploadUrls = ref<Record<string, string>>({}); // URL
// ========== Schema ==========
const querySchema = ref([]);
// ========== ==========
const enumData = reactive({
loading: true,
list: [], //
});
//todo
const hiddenColumns = ref(['fun_code', 'graduallyIntervalTime', 'funMsgTitle', 'funImg', 'createdAt', 'updatedBy', 'updatedAt', 'userPassword', 'userId', 'userSys', 'deletedFlag', "userFace"])
const editFields = ['userId', 'createdAt', 'createdBy', 'updatedAt', 'updatedBy', 'deletedFlag'];
// ========== ==========
function isImageField(field: string) {
return /img|face|picture/i.test(field);
}
// ========== ==========
async function initEnumData() {
try {
enumData.loading = true;
//
const res = await deviceApi.enumList({});
const enums = res.result || res;
//
enumData.list = enums;
// queryFields schema`
querySchema.value = formSchema.filter(formItem => {
// 2.
if (hiddenColumns.value.includes(formItem.fieldName)) {
console.log('跳过隐藏字段:', formItem.fieldName)
return false;
}
return true;
}).map(formSchemaTmp => {
//
const matchedEnums = enums.filter(item => item.fieldName === formSchemaTmp.fieldName);
// fieldName options
const allOptions = matchedEnums.reduce((acc, item) => {
if (item.options && Array.isArray(item.options)) {
return [...acc, ...item.options];
}
return acc;
}, []);
//
if (allOptions.length > 0) {
return {
component: 'Select',
fieldName: formSchemaTmp.fieldName,
label: formSchemaTmp.label,
componentProps: {
placeholder: `请选择`,
allowClear: true,
options: allOptions.map(opt => ({
label: opt.label,
value: String(opt.value), // value
})),
},
};
} else {
// 使 Input
return {
component: 'Input',
fieldName: formSchemaTmp.fieldName,
label: formSchemaTmp.label,
componentProps: {
placeholder: `请输入${formSchemaTmp.label}`,
allowClear: true,
},
};
}
});
} catch (error) {
} finally {
enumData.loading = false;
}
}
//
onMounted(() => {
initEnumData();
});
// ========== ( schema) ==========
const [QueryForm, queryFormApi] = useVbenForm({
schema: [], //
layout: 'inline',
showDefaultActions: false,
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-5',
})
// ========== querySchema ==========
watch(
querySchema,
(newSchema) => {
if (newSchema && newSchema.length > 0) {
console.log('🔄 更新查询表单 schema:', newSchema);
// 使 setState schema
queryFormApi.setState({
schema: newSchema,
});
}
},
{immediate: false}
);
//
const currentPageRef = ref(1);
// ========== ==========
function getFullUrl(url: string) {
if (!url) return '';
if (url.startsWith('http')) return url;
return `${API_BASE_URL}${url}`;
}
const gridOptions = {
columns: [
...columns.map(col => {
// ( img, face, picture)使 +
if (isImageField(col.field)) {
return {
...col,
width: 200,
align: 'center',
slots: {
default: ({row}: any) => {
const imgUrl = row[col.field];
const handleClick = (e: Event) => {
e.stopPropagation();
currentRow.value = row;
isEdit.value = true;
openUploadDialog(col.field, imgUrl);
};
if (!imgUrl) {
return h('div', { class: 'flex items-center justify-center gap-2 h-[50px]' }, [
h('span', { class: 'text-gray-400 text-xs' }, '无图片'),
h('button', { onClick: handleClick, class: 'text-blue-500 text-xs' }, '上传'),
]);
}
return h('div', { class: 'flex items-center gap-2 justify-center' }, [
h(Image, {
src: getFullUrl(imgUrl),
width: 80,
height: 60,
preview: true,
}),
h('button', { onClick: handleClick, class: 'text-blue-500 text-xs' }, '更换'),
]);
}
}
};
}
return col;
}),
{
field: 'action',
title: '操作',
width: 120,
fixed: 'right',
slots: {default: 'action'},
},
],
proxyConfig: {
ajax: {
query: async ({page}) => {
try {
//
const queryValues = await queryFormApi.getValues();
console.log('=== 查询参数 ===', {
pageNum: page?.currentPage || 1,
pageSize: page?.pageSize || 10,
currentPageRef: currentPageRef.value,
...queryValues,
})
const res = await deviceApi.page({
pageNum: page.currentPage || 1,
pageSize: page?.pageSize || 10,
...queryValues, //
})
const data = res.error?.result || res.result || res
return {
items: data.records || [],
total: data.total || 0
}
} catch (error) {
console.error('查询列表失败:', error)
throw error
}
}
},
response: {
result: 'items',
total: 'total'
}
},
pagerConfig: {
enabled: true,
pageSize: 10,
},
showOverflow: true,
minHeight: '100%',
maxHeight: 'auto',
showHeaderOverflow: true,
}
const [Grid, gridApi] = useVbenVxeGrid({gridOptions})
// ========== Schema ==========
const editFormSchema = formSchema.filter(item => !editFields.includes(item.fieldName));
// ========== ==========
const [Modal, modalApi] = useVbenModal({
centered: true,
closable: true,
maskClosable: false,
draggable: true,
width: 800,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
let loadingMessage: any = null;
try {
const values = await formApi.validateAndSubmitForm();
if (!values) return;
// URL
const submitValues = !isEdit.value
? {...values, ...formUploadUrls.value}
: values;
// Day.js
const finalSubmitValues = {
...submitValues,
userBirthday: submitValues.userBirthday ? dayjs(submitValues.userBirthday).format('YYYY-MM-DD') : null,
};
// loading
loadingMessage = message.loading(isEdit.value ? '保存中...' : '新增中...', 0);
if (isEdit.value && currentRow.value?.id) {
await deviceApi.save({...finalSubmitValues, id: currentRow.value.id});
Object.assign(currentRow.value, finalSubmitValues);
modalApi.close();
gridApi.reloadRow(currentRow.value);
loadingMessage();
isEdit.value = false;
message.success('保存成功!');
} else {
await deviceApi.add(finalSubmitValues);
// reload
modalApi.close();
gridApi.reload();
formUploadUrls.value = {};
loadingMessage();
message.success('新增成功!');
}
} catch (error) {
console.error('保存失败:', error);
if (loadingMessage) {
loadingMessage();
}
}
},
onOpenChange(isOpen: boolean) {
if (!isOpen && !isEdit.value) {
formApi.resetForm();
formUploadUrls.value = {};
}
},
})
// ========== ==========
const uploadFileList = ref<any[]>([]);
const [UploadModal, uploadModalApi] = useVbenModal({
centered: true,
closable: true,
maskClosable: false,
draggable: true,
width: 600,
title: '上传图片',
onCancel() {
uploadModalApi.close();
uploadFileList.value = [];
uploadedUrl.value = '';
},
onConfirm: async () => {
let loadingMessage: any = null;
console.log('=== 点击确认保存 ===', {
uploadedUrl: uploadedUrl.value,
isEdit: isEdit.value,
currentRowId: currentRow.value?.id,
uploadFieldName: uploadFieldName.value,
currentRowData: currentRow.value,
});
// URL
if (uploadedUrl.value) {
try {
// URL
if (currentRow.value) {
currentRow.value[uploadFieldName.value] = uploadedUrl.value;
console.log('✅ 已更新当前行图片字段:', {
fieldName: uploadFieldName.value,
newUrl: uploadedUrl.value,
updatedRow: currentRow.value,
});
}
// save
if (isEdit.value && currentRow.value?.id) {
const submitData = {
...currentRow.value,
id: currentRow.value.id
};
console.log('📤 提交保存数据:', submitData);
// loading
loadingMessage = message.loading('保存图片中...', 0);
await deviceApi.save(submitData);
message.success('保存成功!');
//
uploadModalApi.close();
// gridApi.reload();
loadingMessage()
//
uploadFileList.value = [];
uploadedUrl.value = '';
} else {
console.warn('⚠️ 不满足保存条件:', {
isEdit: isEdit.value,
hasId: !!currentRow.value?.id,
});
message.warning('非编辑模式或无 ID,仅更新预览');
//
uploadModalApi.close();
// gridApi.reload();
}
} catch (error: any) {
console.error('❌ 保存失败:', error);
if (loadingMessage) {
loadingMessage();
}
message.error(error?.message || '保存失败');
}
} else {
message.warning('请先选择并上传图片');
}
},
})
// ========== Schema ==========
// id
// id
const getEditFormSchema = () => {
return formSchema.filter(item => {
// editFields
if (!editFields.includes(item.fieldName)) {
// id
if (!isEdit.value && item.fieldName === 'id') {
return false;
}
return true;
}
return false;
}).map(item => {
//
if (!isEdit.value && isImageField(item.fieldName)) {
// 使 formUploadUrls URL使
const currentImageUrl = formUploadUrls.value[item.fieldName] || '';
return {
...item,
component: 'Upload',
componentProps: {
class: 'w-full',
accept: 'image/*',
maxCount: 1,
listType: 'picture-card',
fileList: currentImageUrl && typeof currentImageUrl === 'string' ? [{
uid: '-1',
name: 'image.png',
status: 'done',
url: currentImageUrl,
}] : (Array.isArray(currentImageUrl) ? currentImageUrl : []),
beforeUpload: async (file: any) => {
console.log('📦 新增表单内上传文件:', file);
try {
const resultUrl = await uploadFile(file);
console.log(item.fieldName + ' 新增表单内上传成功,URL:', resultUrl);
message.success('上传成功!');
// URL
formUploadUrls.value[item.fieldName] = resultUrl;
// schema fileList
setTimeout(() => {
formApi.setState({
schema: getEditFormSchema(),
});
}, 0);
// Deleted:formApi.resetForm();
// false
return false;
} catch (error: any) {
console.error('❌ 新增表单内上传失败:', error);
message.error(error?.message || '上传失败');
return false;
}
},
},
};
}
return item;
});
};
// ========== ==========
const [Form, formApi] = useVbenForm({
schema: editFormSchema,
showDefaultActions: false,
wrapperClass: 'grid-cols-1 md:grid-cols-2',
commonConfig: {
componentProps: {
class: 'w-full',
autocomplete: 'off',
},
},
})
// ========== ==========
async function uploadFile(file: File) {
let loadingMessage: any = null;
// loading
loadingMessage = message.loading('图片上传中...', 0);
const formData = new FormData();
formData.append('file', file);
const res = await deviceApi.upload(formData);
loadingMessage();
return res.result || res.message || res.url || res;
}
// ========== ==========
function openUploadDialog(fieldName: string, currentUrl: string) {
console.log('=== 打开上传对话框 ===', {
fieldName,
currentUrl,
isEdit: isEdit.value,
currentRowId: currentRow.value?.id,
currentRowFull: currentRow.value,
});
uploadFieldName.value = fieldName;
uploadImageUrl.value = currentUrl || '';
uploadedUrl.value = ''; // URL
uploadFileList.value = []; //
uploadModalApi.open();
}
// ========== - 使 custom-request ==========
async function handleCustomRequest(options: any) {
const {file, onSuccess, onError} = options;
try {
console.log('📦 开始上传文件:', file);
console.log('📦 文件详情:', {
name: file.name,
type: file.type,
size: file.size,
lastModified: file.lastModified,
});
const resultUrl = await uploadFile(file);
console.log('✅ 上传成功,URL:', resultUrl);
message.success('上传成功!');
// URL
uploadedUrl.value = resultUrl;
console.log('=== uploadedUrl 赋值后 ===', uploadedUrl.value);
//
uploadFileList.value = [{
uid: file.uid,
name: file.name,
status: 'done',
url: resultUrl,
}];
//
onSuccess({url: resultUrl});
} catch (error: any) {
console.error('❌ 上传失败:', error);
message.error(error?.message || '上传失败');
onError(error);
}
}
// ========== ==========
function handleAdd() {
isEdit.value = false;
modalTitle.value = '功能配置表新增';
// URL
formUploadUrls.value = {};
// schema ( id)
formApi.setState({
schema: getEditFormSchema(),
});
formApi.resetForm();
modalApi.open();
}
// ========== ==========
function handleEdit(row: any) {
isEdit.value = true;
currentRow.value = row;
modalTitle.value = '功能配置表编辑';
// schema ( id)
formApi.setState({
schema: getEditFormSchema(),
});
// editFields
const formValues: any = {};
editFormSchema.forEach(item => {
const field = item.fieldName;
if (row[field] !== undefined && row[field] !== null) {
formValues[field] = row[field];
}
});
formApi.setValues(formValues);
modalApi.open();
}
// ========== ==========
function handleDelete(row: any) {
if (!row.id) return;
window.confirm(`确定要删除 "${row.userName}" 吗?`) &&
deviceApi.remove(row.id).then(() => {
gridApi.reload();
});
}
// ========== ==========
function handleSearch() {
console.log('=== 点击查询按钮 ===')
//
gridApi.query();
}
// ========== ==========
function handleReset() {
queryFormApi.resetForm();
gridApi.reload();
}
</script>
<template>
<div
style="height: 100vh; padding: 16px; box-sizing: border-box; display: flex; flex-direction: column;">
<!-- 查询表单 -->
<div class="bg-card mb-4 p-4 rounded shadow flex-shrink-0">
<h3 class="text-lg font-semibold mb-3">查询条件</h3>
<div v-if="enumData.loading" class="text-center py-4 text-gray-500">
正在加载查询条件...
</div>
<QueryForm v-else/>
<div class="mt-3 flex gap-2">
<button @click="handleSearch"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
🔍 查询
</button>
<button @click="handleReset"
class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
🔄 重置
</button>
</div>
</div>
<!-- 数据表格 (可滚动区域) -->
<div class="flex-1 overflow-hidden bg-card rounded shadow">
<Grid>
<template #toolbar-tools>
<button @click="handleAdd" class="mr-2"> 新增</button>
<button @click="() => gridApi.reload()">🔄 刷新</button>
</template>
<template #action="{ row }">
<div class="flex gap-2">
<button @click="() => handleEdit(row)" class="text-blue-500 hover:text-blue-700"> 编辑
</button>
<button @click="() => handleDelete(row)" class="text-red-500 hover:text-red-700">🗑 删除
</button>
</div>
</template>
</Grid>
</div>
<Modal :title="modalTitle">
<Form/>
</Modal>
<!-- 上传对话框 -->
<UploadModal>
<div style="padding: 20px;margin-top: 16px; text-align: center;">
<Upload
name="file"
:file-list="uploadFileList"
:custom-request="handleCustomRequest"
:show-upload-list="true"
accept="image/*" style="width: 100%;"
>
<div
style="width: 100%; height: 150px; border: 2px dashed #d9d9d9; border-radius: 8px; background: #fafafa; display: flex; flex-direction: column; justify-content: center; align-items: center; cursor: pointer;">
<!-- 使用 SVG 图标代替 -->
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#1890ff"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
style="margin-bottom: 8px;">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<span style="color: #666;">点击或拖拽图片到此上传</span>
<span style="color: #999; font-size: 12px; margin-top: 4px;">支持 JPGPNG 格式</span>
</div>
</Upload>
<!-- 展示上传成功后的图片 -->
<div v-if="uploadedUrl" style="margin-top: 16px; text-align: center;">
<p style="color: #67c23a; font-size: 14px; margin-bottom: 8px;"> 上传成功</p>
<p style="color: #999; font-size: 12px; margin-bottom: 8px;">新图片预览:</p>
<Image
:src="uploadedUrl"
style="max-width: 200px; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);"
:preview="true"
/>
<p style="color: #999; font-size: 12px; margin-top: 8px; word-break: break-all;"></p>
</div>
<!-- 展示原图如果有 -->
<div v-if="uploadImageUrl && !uploadedUrl" style="margin-top: 16px; text-align: center;">
<p style="color: #999; font-size: 12px; margin-bottom: 8px;">当前图片:</p>
<Image
:src="getFullUrl(uploadImageUrl)"
style="max-width: 200px; border-radius: 4px;"
:preview="true"
/>
</div>
</div>
</UploadModal>
</div>
</template>
<style scoped>
:deep(.vxe-pager--wrapper) {
margin-bottom: 0.5rem;
}
</style>
Loading…
Cancel
Save