add #13

Merged
p4zgomatu merged 12 commits from develop into main 3 months ago

@ -1,7 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="4bee2ad7-6bf7-4a48-9f02-04204a99b147" name="Changes" comment="" /> <list default="true" id="4bee2ad7-6bf7-4a48-9f02-04204a99b147" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -15,10 +20,10 @@
</option> </option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="ProjectColorInfo"><![CDATA[{ <component name="ProjectColorInfo">{
"customColor": "", &quot;customColor&quot;: &quot;&quot;,
"associatedIndex": 1 &quot;associatedIndex&quot;: 1
}]]></component> }</component>
<component name="ProjectId" id="2wPHHFebSwYCHvl09mTI9f7jYjH" /> <component name="ProjectId" id="2wPHHFebSwYCHvl09mTI9f7jYjH" />
<component name="ProjectViewState"> <component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
@ -28,7 +33,7 @@
"keyToString": { "keyToString": {
"RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true", "RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "feature/lxf", "git-widget-placeholder": "develop",
"kotlin-language-version-configured": "true", "kotlin-language-version-configured": "true",
"last_opened_file_path": "D:/SC/Nursing-home-management-system", "last_opened_file_path": "D:/SC/Nursing-home-management-system",
"node.js.detected.package.eslint": "true", "node.js.detected.package.eslint": "true",
@ -37,6 +42,7 @@
"node.js.selected.package.tslint": "(autodetect)", "node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm", "nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "MavenSettings", "settings.editor.selected.configurable": "MavenSettings",
"ts.external.directory.path": "D:\\xx\\Nursing-home-management-system\\client\\node_modules\\typescript\\lib",
"vue.rearranger.settings.migration": "true" "vue.rearranger.settings.migration": "true"
} }
}]]></component> }]]></component>
@ -57,6 +63,7 @@
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1742217574434</updated> <updated>1742217574434</updated>
<workItem from="1745936199577" duration="445000" /> <workItem from="1745936199577" duration="445000" />
<workItem from="1745995470593" duration="507000" />
</task> </task>
<servers /> <servers />
</component> </component>

@ -18,6 +18,7 @@
"cz-customizable": "^7.0.0", "cz-customizable": "^7.0.0",
"echarts": "^5.4.1", "echarts": "^5.4.1",
"element-plus": "^2.2.28", "element-plus": "^2.2.28",
"file-saver": "^2.0.5",
"global": "^4.4.0", "global": "^4.4.0",
"husky": "^8.0.2", "husky": "^8.0.2",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
@ -26,11 +27,13 @@
"unplugin-vue-components": "^0.22.12", "unplugin-vue-components": "^0.22.12",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-property-decorator": "^9.1.2",
"vue-router": "^4.0.3", "vue-router": "^4.0.3",
"vuex": "^4.0.0", "vuex": "^4.0.0",
"vuex-persistedstate": "^4.1.0" "vuex-persistedstate": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/file-saver": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0", "@typescript-eslint/parser": "^5.4.0",
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.0",
@ -2982,6 +2985,13 @@
"@types/range-parser": "*" "@types/range-parser": "*"
} }
}, },
"node_modules/@types/file-saver": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/@types/file-saver/-/file-saver-2.0.7.tgz",
"integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/html-minifier-terser": { "node_modules/@types/html-minifier-terser": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -4940,18 +4950,6 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/acorn-jsx/node_modules/acorn": {
"version": "7.4.1",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-node": { "node_modules/acorn-node": {
"version": "1.8.2", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
@ -8299,6 +8297,12 @@
"node": "^10.12.0 || >=12.0.0" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
"license": "MIT"
},
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@ -15708,6 +15712,16 @@
"@vue/shared": "3.2.45" "@vue/shared": "3.2.45"
} }
}, },
"node_modules/vue-class-component": {
"version": "7.2.6",
"resolved": "https://registry.npmmirror.com/vue-class-component/-/vue-class-component-7.2.6.tgz",
"integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"vue": "^2.0.0"
}
},
"node_modules/vue-demi": { "node_modules/vue-demi": {
"version": "0.13.11", "version": "0.13.11",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
@ -15861,6 +15875,16 @@
"node": ">=8.9.0" "node": ">=8.9.0"
} }
}, },
"node_modules/vue-property-decorator": {
"version": "9.1.2",
"resolved": "https://registry.npmmirror.com/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz",
"integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==",
"license": "MIT",
"peerDependencies": {
"vue": "*",
"vue-class-component": "*"
}
},
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.1.6", "version": "4.1.6",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz",
@ -18548,6 +18572,12 @@
"@types/range-parser": "*" "@types/range-parser": "*"
} }
}, },
"@types/file-saver": {
"version": "2.0.7",
"resolved": "https://registry.npmmirror.com/@types/file-saver/-/file-saver-2.0.7.tgz",
"integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
"dev": true
},
"@types/html-minifier-terser": { "@types/html-minifier-terser": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -19967,14 +19997,7 @@
"resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true, "dev": true,
"requires": {}, "requires": {}
"dependencies": {
"acorn": {
"version": "7.4.1",
"dev": true,
"peer": true
}
}
}, },
"acorn-node": { "acorn-node": {
"version": "1.8.2", "version": "1.8.2",
@ -22289,6 +22312,11 @@
"flat-cache": "^3.0.4" "flat-cache": "^3.0.4"
} }
}, },
"file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
},
"fill-range": { "fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@ -27373,6 +27401,13 @@
} }
} }
}, },
"vue-class-component": {
"version": "7.2.6",
"resolved": "https://registry.npmmirror.com/vue-class-component/-/vue-class-component-7.2.6.tgz",
"integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==",
"peer": true,
"requires": {}
},
"vue-demi": { "vue-demi": {
"version": "0.13.11", "version": "0.13.11",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
@ -27464,6 +27499,12 @@
} }
} }
}, },
"vue-property-decorator": {
"version": "9.1.2",
"resolved": "https://registry.npmmirror.com/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz",
"integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==",
"requires": {}
},
"vue-router": { "vue-router": {
"version": "4.1.6", "version": "4.1.6",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz",

@ -1,35 +1,57 @@
// 从 "@/utils" 模块中导入 http 对象,用于发起 HTTP 请求
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块中导入 IPageSearchElderByKey 接口
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 定义一个名为 IPageAccidentByKey 的接口,用于分页查询事故登记时的数据结构
interface IPageAccidentByKey { interface IPageAccidentByKey {
// 老人姓名
elderName: string; elderName: string;
// 护工姓名
staffName: string; staffName: string;
} }
// 定义一个名为 IAddAccident 的接口,用于新增事故登记时的数据结构
interface IAddAccident { interface IAddAccident {
// 事故登记的 ID
id: number; id: number;
// 老人的 ID
elderId: number; elderId: number;
// 护工的 ID
staffId: number; staffId: number;
// 事故发生日期
occurDate: string; occurDate: string;
// 事故描述
description: string; description: string;
// 事故相关图片
picture: string; picture: string;
} }
// 定义一个名为 IGetAccidentById 的接口,用于根据编号获取事故登记时的数据结构
interface IGetAccidentById { interface IGetAccidentById {
// 事故登记的 ID
accidentId: string; accidentId: string;
} }
// 定义一个名为 IEditAccident 的接口,用于编辑事故登记时的数据结构
interface IEditAccident { interface IEditAccident {
// 事故登记的 ID
id: number; id: number;
// 姓名
name: string; name: string;
// 电话号码
phone: string; phone: string;
// 关系
relation: string; relation: string;
// 事故日期字符串
accidentDateStr: string; accidentDateStr: string;
// 事故编号
accidentNum: number; accidentNum: number;
} }
// 分页查询事故登记 // 定义一个异步函数 pageAccidentByKey用于分页查询事故登记
export async function pageAccidentByKey(data: IPageAccidentByKey) { export async function pageAccidentByKey(data: IPageAccidentByKey) {
// 使用 http 对象发起 GET 请求,请求路径为 "/api/accident/pageAccidentByKey",并将 data 对象展开作为请求参数
return http.get("/api/accident/pageAccidentByKey", { return http.get("/api/accident/pageAccidentByKey", {
params: { params: {
...data ...data
@ -37,8 +59,9 @@ export async function pageAccidentByKey(data: IPageAccidentByKey) {
}); });
} }
// 分页搜索老人 // 定义一个异步函数 pageSearchElderByKey用于分页搜索老人
export async function pageSearchElderByKey(data: IPageSearchElderByKey) { export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用 http 对象发起 GET 请求,请求路径为 "/api/accident/pageSearchElderByKey",并将 data 对象展开作为请求参数
return http.get("/api/accident/pageSearchElderByKey", { return http.get("/api/accident/pageSearchElderByKey", {
params: { params: {
...data ...data
@ -46,18 +69,21 @@ export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
}); });
} }
// 获取护工列表 // 定义一个异步函数 listAccidentStaff用于获取护工列表
export async function listAccidentStaff() { export async function listAccidentStaff() {
// 使用 http 对象发起 GET 请求,请求路径为 "/api/accident/listAccidentStaff"
return http.get("/api/accident/listAccidentStaff"); return http.get("/api/accident/listAccidentStaff");
} }
// 新增事故登记 // 定义一个函数 addAccident用于新增事故登记
export function addAccident(data: IAddAccident) { export function addAccident(data: IAddAccident) {
// 使用 http 对象发起 POST 请求,请求路径为 "/api/accident/addAccident",并将 data 对象作为请求体
return http.post("/api/accident/addAccident", data); return http.post("/api/accident/addAccident", data);
} }
// 根据编号获取事故登记 // 定义一个异步函数 getAccidentById用于根据编号获取事故登记
export async function getAccidentById(data: IGetAccidentById) { export async function getAccidentById(data: IGetAccidentById) {
// 使用 http 对象发起 GET 请求,请求路径为 "/api/accident/getAccidentById",并将 data 对象展开作为请求参数
return http.get("/api/accident/getAccidentById", { return http.get("/api/accident/getAccidentById", {
params: { params: {
...data ...data
@ -65,13 +91,15 @@ export async function getAccidentById(data: IGetAccidentById) {
}); });
} }
// 编辑事故登记 // 定义一个函数 editAccident用于编辑事故登记
export function editAccident(data: IEditAccident) { export function editAccident(data: IEditAccident) {
// 使用 http 对象发起 PUT 请求,请求路径为 "/api/accident/editAccident",并将 data 对象作为请求体
return http.put("/api/accident/editAccident", data); return http.put("/api/accident/editAccident", data);
} }
// 删除事故登记 // 定义一个异步函数 deleteAccident用于删除事故登记
export async function deleteAccident(data: IGetAccidentById) { export async function deleteAccident(data: IGetAccidentById) {
// 使用 http 对象发起 DELETE 请求,请求路径为 "/api/accident/deleteAccident",并将 data 对象展开作为请求参数
return http.delete("/api/accident/deleteAccident", { return http.delete("/api/accident/deleteAccident", {
params: { params: {
...data ...data

@ -1,18 +1,32 @@
// 从 "@/utils" 模块导入 http 对象,该对象可能封装了 HTTP 请求方法
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个名为 IListRoomByKey 的接口,用于描述获取房间列表时请求参数的数据结构
interface IListRoomByKey { interface IListRoomByKey {
// 楼栋 ID类型为字符串
buildingId: string; buildingId: string;
// 楼层 ID类型为字符串
floorId: string; floorId: string;
// 老人姓名,类型为字符串
elderName: string; elderName: string;
} }
// 获取楼栋列表 /**
*
* @returns Promise
*/
export async function listBuilding() { export async function listBuilding() {
// 发起一个 GET 请求到指定的 API 端点,用于获取楼栋列表
return http.get("/api/bedPanorama/listBuilding"); return http.get("/api/bedPanorama/listBuilding");
} }
// 获取楼层列表 /**
* ID
* @param buildingId - ID
* @returns Promise
*/
export function listFloorByBuildingId(buildingId: string) { export function listFloorByBuildingId(buildingId: string) {
// 发起一个 GET 请求到指定的 API 端点,携带楼栋 ID 作为参数,用于获取对应楼栋的楼层列表
return http.get("/api/bedPanorama/listFloorByBuildingId", { return http.get("/api/bedPanorama/listFloorByBuildingId", {
params: { params: {
buildingId buildingId
@ -20,8 +34,13 @@ export function listFloorByBuildingId(buildingId: string) {
}); });
} }
// 获取房间列表 /**
*
* @param data - IListRoomByKey
* @returns Promise
*/
export function listRoomByKey(data: IListRoomByKey) { export function listRoomByKey(data: IListRoomByKey) {
// 发起一个 GET 请求到指定的 API 端点,将 data 对象中的属性展开作为请求参数,用于获取符合条件的房间列表
return http.get("/api/bedPanorama/listRoomByKey", { return http.get("/api/bedPanorama/listRoomByKey", {
params: { params: {
...data ...data

@ -1,45 +1,79 @@
// 从 '@/utils' 模块导入 http 对象,该对象可能封装了 HTTP 请求的方法,用于后续发起网络请求
import { http } from '@/utils' import { http } from '@/utils'
// 定义一个接口 ISearchFormReserveByKey用于描述分页查询预定时请求参数的数据结构
interface ISearchFormReserveByKey { interface ISearchFormReserveByKey {
// 当前页码,类型为数字
pageNum: number pageNum: number
// 每页显示的数量,类型为数字
pageSize: number pageSize: number
// 老人姓名,可选参数,类型为字符串
elderName?: string elderName?: string
// 付款人电话,可选参数,类型为字符串
payerPhone?: string payerPhone?: string
} }
// 定义一个导出的接口 IPageSearchElderByKey用于描述分页搜索老人时请求参数的数据结构
export interface IPageSearchElderByKey { export interface IPageSearchElderByKey {
// 当前页码,类型为数字
pageNum: number pageNum: number
// 每页显示的数量,类型为数字
pageSize: number pageSize: number
// 老人姓名,可选参数,类型为字符串
elderName?: string elderName?: string
// 老人电话,可选参数,类型为字符串
elderPhone?: string elderPhone?: string
} }
// 定义一个接口 IAddReserve用于描述新增预定时请求体的数据结构
interface IAddReserve { interface IAddReserve {
// 床位 ID类型为字符串
bedId: string bedId: string
// 定金,类型为字符串
deposit: string deposit: string
// 截止日期,类型为字符串
dueDate: string dueDate: string
// 老人地址,类型为字符串
elderAddress: string elderAddress: string
// 老人年龄,类型为字符串
elderAge: string elderAge: string
// 老人姓名,类型为字符串
elderName: string elderName: string
// 老人电话,类型为字符串
elderPhone: string elderPhone: string
// 老人性别,类型为字符串
elderSex: string elderSex: string
// 身份证号码,类型为字符串
idNum: string idNum: string
// 付款人姓名,类型为字符串
payerName: string payerName: string
// 付款人电话,类型为字符串
payerPhone: string payerPhone: string
// 工作人员 ID类型为字符串
staffId: string staffId: string
} }
// 定义一个接口 IGetReserveById用于描述根据预定编号和老人编号获取预定信息时请求参数的数据结构
interface IGetReserveById { interface IGetReserveById {
// 老人 ID类型为字符串
elderId: string elderId: string
// 预定 ID类型为字符串
reserveId: string reserveId: string
} }
// 定义一个接口 IRefund用于描述退款时请求体的数据结构
interface IRefund { interface IRefund {
// 预定 ID类型为字符串
reserveId: string reserveId: string
} }
// 分页查询预定 /**
*
* @param data - ISearchFormReserveByKey
* @returns Promise
*/
export async function pageReserveByKey(data: ISearchFormReserveByKey) { export async function pageReserveByKey(data: ISearchFormReserveByKey) {
// 发起一个 GET 请求到指定的 API 端点,将 data 对象中的属性展开作为请求参数
return http.get('/api/reserve/pageReserveByKey', { return http.get('/api/reserve/pageReserveByKey', {
params: { params: {
...data ...data
@ -47,8 +81,13 @@ export async function pageReserveByKey(data: ISearchFormReserveByKey) {
}) })
} }
// 分页搜索老人 /**
*
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export function pageSearchElderByKey(data: IPageSearchElderByKey) { export function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 发起一个 GET 请求到指定的 API 端点,将 data 对象中的属性展开作为请求参数
return http.get('/api/reserve/pageSearchElderByKey', { return http.get('/api/reserve/pageSearchElderByKey', {
params: { params: {
...data ...data
@ -56,23 +95,41 @@ export function pageSearchElderByKey(data: IPageSearchElderByKey) {
}) })
} }
// 获取营销人员 /**
*
* @returns Promise
*/
export function listReserveStaff() { export function listReserveStaff() {
// 发起一个 GET 请求到指定的 API 端点,用于获取营销人员列表
return http.get('/api/reserve/listReserveStaff') return http.get('/api/reserve/listReserveStaff')
} }
// 获取楼栋树 /**
*
* @returns Promise
*/
export function getBuildTree() { export function getBuildTree() {
// 发起一个 GET 请求到指定的 API 端点,用于获取楼栋树信息
return http.get('/api/reserve/getBuildTree') return http.get('/api/reserve/getBuildTree')
} }
// 新增预定 /**
*
* @param data - IAddReserve
* @returns Promise
*/
export function addReserve(data: IAddReserve) { export function addReserve(data: IAddReserve) {
// 发起一个 POST 请求到指定的 API 端点,将 data 对象作为请求体
return http.post('/api/reserve/addReserve', data) return http.post('/api/reserve/addReserve', data)
} }
// 根据预定编号和老人编号获取预定信息 /**
*
* @param data - IGetReserveById
* @returns Promise
*/
export function getReserveById(data: IGetReserveById) { export function getReserveById(data: IGetReserveById) {
// 发起一个 GET 请求到指定的 API 端点,将 data 对象中的属性展开作为请求参数
return http.get('/api/reserve/getReserveByReserveIdAndElderId', { return http.get('/api/reserve/getReserveByReserveIdAndElderId', {
params: { params: {
...data ...data
@ -80,7 +137,12 @@ export function getReserveById(data: IGetReserveById) {
}) })
} }
// 退款 /**
* 退
* @param data - 退 ID IRefund
* @returns Promise退
*/
export function refund(data: IRefund) { export function refund(data: IRefund) {
return http.put('/api/reserve/refund',data) // 发起一个 PUT 请求到指定的 API 端点,将 data 对象作为请求体
return http.put('/api/reserve/refund', data)
} }

@ -1,64 +1,103 @@
// 从 "@/utils" 模块导入 http 对象,用于发起 HTTP 请求
import { http } from "@/utils"; import { http } from "@/utils";
// 定义 IPageBedByKey 接口,用于分页查询床位时的参数结构
interface IPageBedByKey { interface IPageBedByKey {
// 楼栋 ID
buildId: string; buildId: string;
// 楼层 ID
floorId: string; floorId: string;
// 房间 ID
roomId: string; roomId: string;
// 床位状态标识
bedFlag: string; bedFlag: string;
} }
// 定义 IAddBuilding 接口,用于新增楼栋时的请求数据结构
interface IAddBuilding { interface IAddBuilding {
// 楼栋 ID
id: string; id: string;
// 楼栋名称
name: string; name: string;
// 楼层数量
floorNum: string; floorNum: string;
} }
// 定义 IGetBuildingById 接口,用于根据编号获取楼栋时的参数结构
interface IGetBuildingById { interface IGetBuildingById {
// 楼栋 ID
buildingId: string; buildingId: string;
} }
// 定义 IAddFloor 接口,用于新增楼层时的请求数据结构
interface IAddFloor { interface IAddFloor {
// 楼层 ID
id: string; id: string;
// 楼层名称
name: string; name: string;
// 房间数量
roomNum: string; roomNum: string;
// 所属楼栋 ID
buildingId: string; buildingId: string;
// 楼层限制相关信息
floorLimit: string; floorLimit: string;
} }
// 定义 IGetFloorById 接口,用于根据编号获取楼层时的参数结构
interface IGetFloorById { interface IGetFloorById {
// 楼层 ID
floorId: string; floorId: string;
} }
// 定义 IAddRoom 接口,用于新增房间时的请求数据结构
interface IAddRoom { interface IAddRoom {
// 房间 ID
id: string; id: string;
// 房间名称
name: string; name: string;
// 房间类型 ID
typeId: string; typeId: string;
// 床位数量
bedNum: string; bedNum: string;
// 所属楼层 ID
floorId: string; floorId: string;
// 房间限制相关信息
roomLimit: string; roomLimit: string;
} }
// 定义 IGetRoomById 接口,用于根据编号获取房间时的参数结构
interface IGetRoomById { interface IGetRoomById {
// 房间 ID
roomId: string; roomId: string;
} }
// 定义 IDeleteNode 接口,用于删除节点时的参数结构
interface IDeleteNode { interface IDeleteNode {
// 节点 ID
id: string; id: string;
// 节点标识
mark: string; mark: string;
} }
// 定义 IAddBed 接口,用于新增床位时的请求数据结构
interface IAddBed { interface IAddBed {
// 床位 ID
id: string; id: string;
// 床位名称
name: string; name: string;
// 所属房间 ID
roomId: string; roomId: string;
// 床位限制相关信息
bedLimit: string; bedLimit: string;
} }
// 定义 IGetBedById 接口,用于根据编号获取床位时的参数结构
interface IGetBedById { interface IGetBedById {
// 床位 ID
bedId: string; bedId: string;
} }
// 床位状态 // 定义床位状态列表,包含每个状态的标签和对应的值
export const IBedFlagList = [ export const IBedFlagList = [
{ label: "空闲", value: "空闲" }, { label: "空闲", value: "空闲" },
{ label: "预定", value: "预定" }, { label: "预定", value: "预定" },
@ -66,13 +105,22 @@ export const IBedFlagList = [
{ label: "退住审核", value: "退住审核" } { label: "退住审核", value: "退住审核" }
]; ];
// 获取楼栋-楼层-房间树 /**
* - -
* @returns Promise - -
*/
export async function getNoBedTree() { export async function getNoBedTree() {
// 发起 GET 请求到 /api/build/getNoBedTree 接口获取数据
return http.get("/api/build/getNoBedTree"); return http.get("/api/build/getNoBedTree");
} }
// 分页查询床位 /**
*
* @param data - IPageBedByKey
* @returns Promise
*/
export async function pageBedByKey(data: IPageBedByKey) { export async function pageBedByKey(data: IPageBedByKey) {
// 发起 GET 请求到 /api/build/pageBedByKey 接口,将 data 对象展开作为请求参数
return http.get("/api/build/pageBedByKey", { return http.get("/api/build/pageBedByKey", {
params: { params: {
...data ...data
@ -80,13 +128,23 @@ export async function pageBedByKey(data: IPageBedByKey) {
}); });
} }
// 新增楼栋 /**
*
* @param data - IAddBuilding
* @returns Promise
*/
export function addBuilding(data: IAddBuilding) { export function addBuilding(data: IAddBuilding) {
// 发起 POST 请求到 /api/build/addBuilding 接口,将 data 作为请求体
return http.post("/api/build/addBuilding", data); return http.post("/api/build/addBuilding", data);
} }
// 根据编号获取楼栋 /**
*
* @param data - IGetBuildingById
* @returns Promise
*/
export async function getBuildingById(data: IGetBuildingById) { export async function getBuildingById(data: IGetBuildingById) {
// 发起 GET 请求到 /api/build/getBuildingById 接口,将 data 对象展开作为请求参数
return http.get("/api/build/getBuildingById", { return http.get("/api/build/getBuildingById", {
params: { params: {
...data ...data
@ -94,18 +152,33 @@ export async function getBuildingById(data: IGetBuildingById) {
}); });
} }
// 编辑楼栋 /**
*
* @param data - IAddBuilding
* @returns Promise
*/
export function editBuilding(data: IAddBuilding) { export function editBuilding(data: IAddBuilding) {
// 发起 PUT 请求到 /api/build/editBuilding 接口,将 data 作为请求体
return http.put("/api/build/editBuilding", data); return http.put("/api/build/editBuilding", data);
} }
// 新增楼层 /**
*
* @param data - IAddFloor
* @returns Promise
*/
export function addFloor(data: IAddFloor) { export function addFloor(data: IAddFloor) {
// 发起 POST 请求到 /api/build/addFloor 接口,将 data 作为请求体
return http.post("/api/build/addFloor", data); return http.post("/api/build/addFloor", data);
} }
// 根据编号获取楼层 /**
*
* @param data - IGetFloorById
* @returns Promise
*/
export async function getFloorById(data: IGetFloorById) { export async function getFloorById(data: IGetFloorById) {
// 发起 GET 请求到 /api/build/getFloorById 接口,将 data 对象展开作为请求参数
return http.get("/api/build/getFloorById", { return http.get("/api/build/getFloorById", {
params: { params: {
...data ...data
@ -113,23 +186,42 @@ export async function getFloorById(data: IGetFloorById) {
}); });
} }
// 编辑楼层 /**
*
* @param data - IAddFloor
* @returns Promise
*/
export function editFloor(data: IAddFloor) { export function editFloor(data: IAddFloor) {
// 发起 PUT 请求到 /api/build/editFloor 接口,将 data 作为请求体
return http.put("/api/build/editFloor", data); return http.put("/api/build/editFloor", data);
} }
// 获取房间类型列表 /**
*
* @returns Promise
*/
export async function listRoomType() { export async function listRoomType() {
// 发起 GET 请求到 /api/build/listRoomType 接口获取数据
return http.get("/api/build/listRoomType"); return http.get("/api/build/listRoomType");
} }
// 新增房间 /**
*
* @param data - IAddRoom
* @returns Promise
*/
export function addRoom(data: IAddRoom) { export function addRoom(data: IAddRoom) {
// 发起 POST 请求到 /api/build/addRoom 接口,将 data 作为请求体
return http.post("/api/build/addRoom", data); return http.post("/api/build/addRoom", data);
} }
// 根据编号获取房间 /**
*
* @param data - IGetRoomById
* @returns Promise
*/
export async function getRoomById(data: IGetRoomById) { export async function getRoomById(data: IGetRoomById) {
// 发起 GET 请求到 /api/build/getRoomById 接口,将 data 对象展开作为请求参数
return http.get("/api/build/getRoomById", { return http.get("/api/build/getRoomById", {
params: { params: {
...data ...data
@ -137,13 +229,23 @@ export async function getRoomById(data: IGetRoomById) {
}); });
} }
// 编辑房间 /**
*
* @param data - IAddRoom
* @returns Promise
*/
export function editRoom(data: IAddRoom) { export function editRoom(data: IAddRoom) {
// 发起 PUT 请求到 /api/build/editRoom 接口,将 data 作为请求体
return http.put("/api/build/editRoom", data); return http.put("/api/build/editRoom", data);
} }
// 删除节点 /**
*
* @param data - IDeleteNode
* @returns Promise
*/
export async function deleteNode(data: IDeleteNode) { export async function deleteNode(data: IDeleteNode) {
// 发起 DELETE 请求到 /api/build/deleteNode 接口,将 data 对象展开作为请求参数
return http.delete("/api/build/deleteNode", { return http.delete("/api/build/deleteNode", {
params: { params: {
...data ...data
@ -151,13 +253,23 @@ export async function deleteNode(data: IDeleteNode) {
}); });
} }
// 新增床位 /**
*
* @param data - IAddBed
* @returns Promise
*/
export function addBed(data: IAddBed) { export function addBed(data: IAddBed) {
// 发起 POST 请求到 /api/build/addBed 接口,将 data 作为请求体
return http.post("/api/build/addBed", data); return http.post("/api/build/addBed", data);
} }
// 根据编号获取床位 /**
*
* @param data - IGetBedById
* @returns Promise
*/
export async function getBedById(data: IGetBedById) { export async function getBedById(data: IGetBedById) {
// 发起 GET 请求到 /api/build/getBedById 接口,将 data 对象展开作为请求参数
return http.get("/api/build/getBedById", { return http.get("/api/build/getBedById", {
params: { params: {
...data ...data
@ -165,13 +277,23 @@ export async function getBedById(data: IGetBedById) {
}); });
} }
// 编辑床位 /**
*
* @param data - IAddBed
* @returns Promise
*/
export function editBed(data: IAddBed) { export function editBed(data: IAddBed) {
// 发起 PUT 请求到 /api/build/editBed 接口,将 data 作为请求体
return http.put("/api/build/editBed", data); return http.put("/api/build/editBed", data);
} }
// 删除床位 /**
*
* @param data - IGetBedById
* @returns Promise
*/
export async function deleteBed(data: IGetBedById) { export async function deleteBed(data: IGetBedById) {
// 发起 DELETE 请求到 /api/build/deleteBed 接口,将 data 对象展开作为请求参数
return http.delete("/api/build/deleteBed", { return http.delete("/api/build/deleteBed", {
params: { params: {
...data ...data

@ -1,26 +1,50 @@
// 从 "@/utils" 模块导入 http 对象,用于发起 HTTP 请求
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/dishes" 模块导入 IPageDishesByKey 接口,用于分页查询菜品时的参数类型定义
import { IPageDishesByKey } from "@/apis/dishes"; import { IPageDishesByKey } from "@/apis/dishes";
/**
* IPageCateringSetByKey
*/
interface IPageCateringSetByKey { interface IPageCateringSetByKey {
name:string; // 名称,用于筛选餐饮套餐
name: string;
// 套餐名称,由于后台与前端组件封装要求,该字段与 name 可能存在关联
setName: string; setName: string;
} }
/**
* IAddCateringSet
*/
interface IAddCateringSet { interface IAddCateringSet {
// 餐饮套餐的 ID
id: number; id: number;
// 餐饮套餐的名称
name: string; name: string;
// 餐饮套餐的月价格
monthPrice: number; monthPrice: number;
dishesIdList: any ; // 菜品 ID 列表,类型为 any需根据实际情况调整
dishesIdList: any;
} }
/**
* IGetCateringSetById
*/
interface IGetCateringSetById { interface IGetCateringSetById {
setId: string // 餐饮套餐的 ID
setId: string;
} }
// 分页查询餐饮套餐 /**
*
* @param data - IPageCateringSetByKey
* @returns Promise
*/
export async function pageCateringSetByKey(data: IPageCateringSetByKey) { export async function pageCateringSetByKey(data: IPageCateringSetByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 由于后台返回的字段与前端表单数据的 prop 不一样,而组件封装要求字段一致
Reflect.has(data, 'name') ? (data.setName = data.name) : '' // 所以如果 data 对象中有 'name' 字段,则将其值赋给 'setName' 字段
Reflect.has(data, 'name') ? (data.setName = data.name) : '';
// 发起 GET 请求到 /api/cateringSet/pageCateringSetByKey 接口,将 data 对象展开作为请求参数
return http.get("/api/cateringSet/pageCateringSetByKey", { return http.get("/api/cateringSet/pageCateringSetByKey", {
params: { params: {
...data ...data
@ -28,13 +52,22 @@ export async function pageCateringSetByKey(data: IPageCateringSetByKey) {
}); });
} }
// 获取菜品分类 /**
*
* @returns Promise
*/
export function listDishesType() { export function listDishesType() {
// 发起 POST 请求到 /api/cateringSet/listDishesType 接口获取菜品分类数据
return http.post("/api/cateringSet/listDishesType"); return http.post("/api/cateringSet/listDishesType");
} }
// 分页查询菜品 /**
*
* @param data - IPageDishesByKey
* @returns Promise
*/
export async function pageDishesByKey(data: IPageDishesByKey) { export async function pageDishesByKey(data: IPageDishesByKey) {
// 发起 GET 请求到 /api/cateringSet/pageDishesByKey 接口,将 data 对象展开作为请求参数
return http.get("/api/cateringSet/pageDishesByKey", { return http.get("/api/cateringSet/pageDishesByKey", {
params: { params: {
...data ...data
@ -42,13 +75,23 @@ export async function pageDishesByKey(data: IPageDishesByKey) {
}); });
} }
// 新增餐饮套餐 /**
*
* @param data - IAddCateringSet
* @returns Promise
*/
export function addCateringSet(data: IAddCateringSet) { export function addCateringSet(data: IAddCateringSet) {
// 发起 POST 请求到 /api/cateringSet/addCateringSet 接口,将 data 作为请求体
return http.post("/api/cateringSet/addCateringSet", data); return http.post("/api/cateringSet/addCateringSet", data);
} }
// 根据编号查询餐饮套餐 /**
*
* @param data - IGetCateringSetById
* @returns Promise
*/
export async function getCateringSetById(data: IGetCateringSetById) { export async function getCateringSetById(data: IGetCateringSetById) {
// 发起 GET 请求到 /api/cateringSet/getCateringSetById 接口,将 data 对象展开作为请求参数
return http.get("/api/cateringSet/getCateringSetById", { return http.get("/api/cateringSet/getCateringSetById", {
params: { params: {
...data ...data
@ -56,13 +99,23 @@ export async function getCateringSetById(data: IGetCateringSetById) {
}); });
} }
// 编辑餐饮套餐 /**
*
* @param data - IAddCateringSet
* @returns Promise
*/
export function editCateringSet(data: IAddCateringSet) { export function editCateringSet(data: IAddCateringSet) {
// 发起 PUT 请求到 /api/cateringSet/editCateringSet 接口,将 data 作为请求体
return http.put("/api/cateringSet/editCateringSet", data); return http.put("/api/cateringSet/editCateringSet", data);
} }
// 删除餐饮套餐 /**
*
* @param data - IGetCateringSetById
* @returns Promise
*/
export async function deleteCateringSet(data: IGetCateringSetById) { export async function deleteCateringSet(data: IGetCateringSetById) {
// 发起 DELETE 请求到 /api/cateringSet/deleteCateringSet 接口,将 data 对象展开作为请求参数
return http.delete("/api/cateringSet/deleteCateringSet", { return http.delete("/api/cateringSet/deleteCateringSet", {
params: { params: {
...data ...data

@ -1,44 +1,87 @@
// 从 "@/utils" 模块导入 http 对象,用于发起 HTTP 请求
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,用于分页搜索老人时的参数类型定义
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
/**
* IPageCheckContractByKey
*/
interface IPageCheckContractByKey { interface IPageCheckContractByKey {
// 姓名,用于筛选入住签约记录
name: string; name: string;
// 性别,用于筛选入住签约记录
sex: string; sex: string;
// 身份证号码,用于筛选入住签约记录
idNum: string; idNum: string;
} }
/**
* IAddCheckContract
*/
interface IAddCheckContract { interface IAddCheckContract {
// 入住签约的 ID
id: string; id: string;
// 护理等级 ID
nursingGradeId: string; nursingGradeId: string;
// 餐饮套餐 ID
cateringSetId: string; cateringSetId: string;
// 床位 ID
bedId: string; bedId: string;
// 姓名
name: string; name: string;
// 身份证号码
idNum: string; idNum: string;
// 年龄
age: string; age: string;
// 性别
sex: string; sex: string;
// 电话
phone: string; phone: string;
// 地址
address: string; address: string;
// 工作人员 ID
staffId: string; staffId: string;
// 签约日期
signDate: string; signDate: string;
// 开始日期
startDate: string; startDate: string;
// 结束日期
endDate: string; endDate: string;
// 紧急联系人查询列表,包含多个紧急联系人信息
operateEmergencyContactQueryList: IEmergencyContact[]; operateEmergencyContactQueryList: IEmergencyContact[];
} }
/**
* IEmergencyContact
*/
interface IEmergencyContact { interface IEmergencyContact {
// 姓名
name: string; name: string;
// 电话
phone: string; phone: string;
// 邮箱
email: string; email: string;
// 关系
relation: string; relation: string;
// 接收标志
receiveFlag: string; receiveFlag: string;
} }
/**
* IGetCheckContractById
*/
interface IGetCheckContractById { interface IGetCheckContractById {
elderId: string // 老人 ID
elderId: string;
} }
// 分页查询入住签约 /**
*
* @param data - IPageCheckContractByKey
* @returns Promise
*/
export async function pageCheckContractByKey(data: IPageCheckContractByKey) { export async function pageCheckContractByKey(data: IPageCheckContractByKey) {
// 发起 GET 请求到 /api/checkContract/pageCheckContractByKey 接口,将 data 对象展开作为请求参数
return http.get("/api/checkContract/pageCheckContractByKey", { return http.get("/api/checkContract/pageCheckContractByKey", {
params: { params: {
...data ...data
@ -46,8 +89,13 @@ export async function pageCheckContractByKey(data: IPageCheckContractByKey) {
}); });
} }
// 分页搜索老人 /**
*
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export async function pageSearchElderByKey(data: IPageSearchElderByKey) { export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 发起 GET 请求到 /api/checkContract/pageSearchElderByKey 接口,将 data 对象展开作为请求参数
return http.get("/api/checkContract/pageSearchElderByKey", { return http.get("/api/checkContract/pageSearchElderByKey", {
params: { params: {
...data ...data
@ -55,13 +103,22 @@ export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
}); });
} }
// 获取护理等级列表 /**
*
* @returns Promise
*/
export async function listNurseGrade() { export async function listNurseGrade() {
// 发起 GET 请求到 /api/checkContract/listNurseGrade 接口获取护理等级数据
return http.get("/api/checkContract/listNurseGrade"); return http.get("/api/checkContract/listNurseGrade");
} }
// 根据编号查询护理等级 /**
*
* @param nurseGradeId - ID
* @returns Promise
*/
export async function getNurseGradeById(nurseGradeId: string) { export async function getNurseGradeById(nurseGradeId: string) {
// 发起 GET 请求到 /api/checkContract/getNurseGradeById 接口,将 nurseGradeId 作为请求参数
return http.get("/api/checkContract/getNurseGradeById", { return http.get("/api/checkContract/getNurseGradeById", {
params: { params: {
nurseGradeId nurseGradeId
@ -69,13 +126,22 @@ export async function getNurseGradeById(nurseGradeId: string) {
}); });
} }
// 获取餐饮套餐列表 /**
*
* @returns Promise
*/
export async function listCateringSet() { export async function listCateringSet() {
// 发起 GET 请求到 /api/checkContract/listCateringSet 接口获取餐饮套餐数据
return http.get("/api/checkContract/listCateringSet"); return http.get("/api/checkContract/listCateringSet");
} }
// 根据编号查询餐饮套餐 /**
*
* @param cateringSetId - ID
* @returns Promise
*/
export async function getCateringSetById(cateringSetId: string) { export async function getCateringSetById(cateringSetId: string) {
// 发起 GET 请求到 /api/checkContract/getCateringSetById 接口,将 cateringSetId 作为请求参数
return http.get("/api/checkContract/getCateringSetById", { return http.get("/api/checkContract/getCateringSetById", {
params: { params: {
cateringSetId cateringSetId
@ -83,13 +149,22 @@ export async function getCateringSetById(cateringSetId: string) {
}); });
} }
// 获取楼栋树 /**
*
* @returns Promise
*/
export async function getBuildTree() { export async function getBuildTree() {
// 发起 GET 请求到 /api/checkContract/getBuildTree 接口获取楼栋树数据
return http.get("/api/checkContract/getBuildTree"); return http.get("/api/checkContract/getBuildTree");
} }
// 根据编号查询床位 /**
*
* @param bedId - ID
* @returns Promise
*/
export async function getBedById(bedId: string) { export async function getBedById(bedId: string) {
// 发起 GET 请求到 /api/checkContract/getBedById 接口,将 bedId 作为请求参数
return http.get("/api/checkContract/getBedById", { return http.get("/api/checkContract/getBedById", {
params: { params: {
bedId bedId
@ -97,18 +172,32 @@ export async function getBedById(bedId: string) {
}); });
} }
// 获取营销人员 /**
*
* @returns Promise
*/
export async function listReserveStaff() { export async function listReserveStaff() {
// 发起 GET 请求到 /api/checkContract/listReserveStaff 接口获取营销人员数据
return http.get("/api/checkContract/listReserveStaff"); return http.get("/api/checkContract/listReserveStaff");
} }
// 新增入住签约 /**
*
* @param data - IAddCheckContract
* @returns Promise
*/
export function addCheckContract(data: IAddCheckContract) { export function addCheckContract(data: IAddCheckContract) {
// 发起 POST 请求到 /api/checkContract/addCheckContract 接口,将 data 作为请求体
return http.post("/api/checkContract/addCheckContract", data); return http.post("/api/checkContract/addCheckContract", data);
} }
// 根据老人编号查询入住签约 /**
*
* @param data - IGetCheckContractById
* @returns Promise
*/
export async function getCheckContractById(data: IGetCheckContractById) { export async function getCheckContractById(data: IGetCheckContractById) {
// 发起 GET 请求到 /api/checkContract/getCheckContractById 接口,将 data 对象展开作为请求参数
return http.get("/api/checkContract/getCheckContractById", { return http.get("/api/checkContract/getCheckContractById", {
params: { params: {
...data ...data
@ -116,13 +205,23 @@ export async function getCheckContractById(data: IGetCheckContractById) {
}); });
} }
// 编辑入住签约 /**
*
* @param data - IAddCheckContract
* @returns Promise
*/
export function editCheckContract(data: IAddCheckContract) { export function editCheckContract(data: IAddCheckContract) {
// 发起 PUT 请求到 /api/checkContract/editCheckContract 接口,将 data 作为请求体
return http.put("/api/checkContract/editCheckContract", data); return http.put("/api/checkContract/editCheckContract", data);
} }
// 删除入住签约 /**
*
* @param data - IGetCheckContractById
* @returns Promise
*/
export async function deleteCheckContract(data: IGetCheckContractById) { export async function deleteCheckContract(data: IGetCheckContractById) {
// 发起 DELETE 请求到 /api/checkContract/deleteCheckContract 接口,将 data 对象展开作为请求参数
return http.delete("/api/checkContract/deleteCheckContract", { return http.delete("/api/checkContract/deleteCheckContract", {
params: { params: {
...data ...data

@ -1,13 +1,25 @@
// 从 "@/utils" 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IPageConsumeByKey用于描述分页查询消费记录时的请求参数结构
interface IPageConsumeByKey { interface IPageConsumeByKey {
// 老人姓名,用于筛选消费记录,类型为字符串
elderName: string; elderName: string;
// 开始时间,用于筛选消费记录的时间范围,类型为字符串
startTime: string; startTime: string;
// 结束时间,用于筛选消费记录的时间范围,类型为字符串
endTime: string; endTime: string;
} }
// 分页查询消费记录 /**
* pageConsumeByKey
* @param data - IPageConsumeByKey
* @returns Promise
*/
export async function pageConsumeByKey(data: IPageConsumeByKey) { export async function pageConsumeByKey(data: IPageConsumeByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/consume/pageConsumeByKey"
// 将传入的 data 对象展开作为请求的参数
return http.get("/api/consume/pageConsumeByKey", { return http.get("/api/consume/pageConsumeByKey", {
params: { params: {
...data ...data

@ -1,24 +1,47 @@
// 从 "@/utils" 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,
// 用于描述分页搜索老人时的请求参数结构
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 定义一个接口 IPageDepositRechargeByKey用于描述分页查询预存充值时的请求参数结构
interface IPageDepositRechargeByKey { interface IPageDepositRechargeByKey {
idNum:string; // 身份证号码,用于筛选预存充值记录,类型为字符串
name:string; idNum: string;
elderName:string; // 姓名,用于筛选预存充值记录,类型为字符串
name: string;
// 老人姓名,用于筛选预存充值记录,类型为字符串
elderName: string;
// 电话,用于筛选预存充值记录,类型为字符串
phone: string; phone: string;
// 老人电话,用于筛选预存充值记录,类型为字符串
elderPhone: string; elderPhone: string;
} }
// 定义一个接口 IRecharge用于描述入住老人账户充值时的请求参数结构
interface IRecharge { interface IRecharge {
// 老人 ID用于指定要充值的老人账户类型为字符串
elderId: string; elderId: string;
// 充值金额,用于指定充值的金额,类型为字符串
amount: string; amount: string;
} }
// 分页查询预存充值 /**
* pageDepositRechargeByKey
* @param data - IPageDepositRechargeByKey
* @returns Promise
*/
export async function pageDepositRechargeByKey(data: IPageDepositRechargeByKey) { export async function pageDepositRechargeByKey(data: IPageDepositRechargeByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
Reflect.has(data, 'elderName') ? (data.name = data.elderName) : '' // 但是组件封装需要一样,所以请求前增加一些这两个字段
Reflect.has(data, 'elderPhone') ? (data.phone = data.elderPhone) : '' // 如果 data 对象中有 'elderName' 字段,则将其值赋给 'name' 字段
Reflect.has(data, 'elderName')? (data.name = data.elderName) : '';
// 如果 data 对象中有 'elderPhone' 字段,则将其值赋给 'phone' 字段
Reflect.has(data, 'elderPhone')? (data.phone = data.elderPhone) : '';
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/depositRecharge/pageDepositRechargeByKey"
// 将处理后的 data 对象展开作为请求的参数
return http.get("/api/depositRecharge/pageDepositRechargeByKey", { return http.get("/api/depositRecharge/pageDepositRechargeByKey", {
params: { params: {
...data ...data
@ -26,16 +49,28 @@ export async function pageDepositRechargeByKey(data: IPageDepositRechargeByKey)
}); });
} }
// 分页搜索老人 /**
* pageSearchElderByKey
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export function pageSearchElderByKey(data: IPageSearchElderByKey) { export function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/depositRecharge/pageSearchElderByKey"
// 将 data 对象展开作为请求的参数
return http.get('/api/depositRecharge/pageSearchElderByKey', { return http.get('/api/depositRecharge/pageSearchElderByKey', {
params: { params: {
...data ...data
} }
}) });
} }
// 入住老人账户充值 /**
* recharge
* @param data - IRecharge
* @returns Promise
*/
export function recharge(data: IRecharge) { export function recharge(data: IRecharge) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/depositRecharge/recharge"
// 将 data 对象作为请求的主体
return http.put("/api/depositRecharge/recharge", data); return http.put("/api/depositRecharge/recharge", data);
} }

@ -1,36 +1,60 @@
// 从 "@/utils" 模块导入名为 http 的对象,该对象可能是用于发起 HTTP 请求的工具,比如对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IListDishesType用于描述获取菜品分类列表时的请求参数结构
interface IListDishesType { interface IListDishesType {
// 菜品分类名称,用于筛选菜品分类列表,类型为字符串
dishesTypeName: string; dishesTypeName: string;
} }
// 导出一个接口 IPageDishesByKey用于描述分页查询菜品时的请求参数结构
export interface IPageDishesByKey { export interface IPageDishesByKey {
// 菜品名称,用于筛选菜品,类型为字符串
dishesName: string; dishesName: string;
// 菜品类型 ID用于筛选属于特定类型的菜品类型为数字
typeId: number; typeId: number;
} }
// 定义一个接口 IAddDishesType用于描述新增菜品分类时的数据结构
interface IAddDishesType { interface IAddDishesType {
// 菜品分类的 ID类型为数字
id: number; id: number;
// 菜品分类的名称,类型为字符串
name: string; name: string;
} }
// 定义一个接口 IGetDishesTypeById用于描述根据编号获取菜品分类时的请求参数结构
interface IGetDishesTypeById { interface IGetDishesTypeById {
// 菜品分类 ID类型为字符串
dishesTypeId: string; dishesTypeId: string;
} }
// 定义一个接口 IAddDishes用于描述新增菜品时的数据结构
interface IAddDishes { interface IAddDishes {
// 菜品的 ID类型为数字
id: number; id: number;
// 菜品的名称,类型为字符串
name: string; name: string;
// 菜品的价格,类型为字符串
price: string; price: string;
// 菜品所属类型的 ID类型为数字
typeId: number; typeId: number;
} }
// 导出一个接口 IGetDishesById用于描述根据编号获取菜品时的请求参数结构
export interface IGetDishesById { export interface IGetDishesById {
// 菜品 ID类型为字符串
dishesId: string; dishesId: string;
} }
// 获取菜品分类列表 /**
* listDishesType
* @param data - IListDishesType
* @returns Promise
*/
export async function listDishesType(data: IListDishesType) { export async function listDishesType(data: IListDishesType) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/dishes/listDishesType"
// 将 data 对象展开作为请求的参数
return http.get("/api/dishes/listDishesType", { return http.get("/api/dishes/listDishesType", {
params: { params: {
...data ...data
@ -38,8 +62,14 @@ export async function listDishesType(data: IListDishesType) {
}); });
} }
// 分页查询菜品 /**
* pageDishesByKey
* @param data - IPageDishesByKey
* @returns Promise
*/
export async function pageDishesByKey(data: IPageDishesByKey) { export async function pageDishesByKey(data: IPageDishesByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/dishes/pageDishesByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/dishes/pageDishesByKey", { return http.get("/api/dishes/pageDishesByKey", {
params: { params: {
...data ...data
@ -47,13 +77,25 @@ export async function pageDishesByKey(data: IPageDishesByKey) {
}); });
} }
// 新增菜品分类 /**
* addDishesType
* @param data - IAddDishesType
* @returns Promise
*/
export function addDishesType(data: IAddDishesType) { export function addDishesType(data: IAddDishesType) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/dishes/addDishesType"
// 将 data 对象作为请求的主体
return http.post("/api/dishes/addDishesType", data); return http.post("/api/dishes/addDishesType", data);
} }
// 根据编号获取菜品分类 /**
* getDishesTypeById
* @param data - IGetDishesTypeById
* @returns Promise
*/
export async function getDishesTypeById(data: IGetDishesTypeById) { export async function getDishesTypeById(data: IGetDishesTypeById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/dishes/getDishesTypeById"
// 将 data 对象展开作为请求的参数
return http.get("/api/dishes/getDishesTypeById", { return http.get("/api/dishes/getDishesTypeById", {
params: { params: {
...data ...data
@ -61,13 +103,25 @@ export async function getDishesTypeById(data: IGetDishesTypeById) {
}); });
} }
// 编辑菜品分类 /**
* editDishesType
* @param data - IAddDishesType
* @returns Promise
*/
export function editDishesType(data: IAddDishesType) { export function editDishesType(data: IAddDishesType) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/dishes/editDishesType"
// 将 data 对象作为请求的主体
return http.put("/api/dishes/editDishesType", data); return http.put("/api/dishes/editDishesType", data);
} }
// 删除菜品分类 /**
* deleteDishesType
* @param data - IGetDishesTypeById
* @returns Promise
*/
export async function deleteDishesType(data: IGetDishesTypeById) { export async function deleteDishesType(data: IGetDishesTypeById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/dishes/deleteDishesType"
// 将 data 对象展开作为请求的参数
return http.delete("/api/dishes/deleteDishesType", { return http.delete("/api/dishes/deleteDishesType", {
params: { params: {
...data ...data
@ -75,13 +129,25 @@ export async function deleteDishesType(data: IGetDishesTypeById) {
}); });
} }
// 新增菜品 /**
* addDishes
* @param data - IAddDishes
* @returns Promise
*/
export function addDishes(data: IAddDishes) { export function addDishes(data: IAddDishes) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/dishes/addDishes"
// 将 data 对象作为请求的主体
return http.post("/api/dishes/addDishes", data); return http.post("/api/dishes/addDishes", data);
} }
// 根据编号获取菜品 /**
* getDishesById
* @param data - IGetDishesById
* @returns Promise
*/
export async function getDishesById(data: IGetDishesById) { export async function getDishesById(data: IGetDishesById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/dishes/getDishesById"
// 将 data 对象展开作为请求的参数
return http.get("/api/dishes/getDishesById", { return http.get("/api/dishes/getDishesById", {
params: { params: {
...data ...data
@ -89,13 +155,25 @@ export async function getDishesById(data: IGetDishesById) {
}); });
} }
// 编辑菜品 /**
* editDishes
* @param data - IAddDishes
* @returns Promise
*/
export function editDishes(data: IAddDishes) { export function editDishes(data: IAddDishes) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/dishes/editDishes"
// 将 data 对象作为请求的主体
return http.put("/api/dishes/editDishes", data); return http.put("/api/dishes/editDishes", data);
} }
// 删除菜品 /**
* deleteDishes
* @param data - IGetDishesById
* @returns Promise
*/
export async function deleteDishes(data: IGetDishesById) { export async function deleteDishes(data: IGetDishesById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/dishes/deleteDishes"
// 将 data 对象展开作为请求的参数
return http.delete("/api/dishes/deleteDishes", { return http.delete("/api/dishes/deleteDishes", {
params: { params: {
...data ...data

@ -1,43 +1,72 @@
// 从 "@/utils" 模块导入名为 http 的对象,该对象可能是对 HTTP 请求库(如 axios的封装用于发起各种 HTTP 请求
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IPageElderByKey用于描述分页查询员工这里可能是长者相关名称可能有误时的请求参数结构
interface IPageElderByKey { interface IPageElderByKey {
// 姓名,用于筛选,类型为字符串
name: string; name: string;
// 长者姓名,用于筛选,类型为字符串
elderName: string; elderName: string;
// 身份证号码,用于筛选,类型为字符串
idNum: string; idNum: string;
// 性别,用于筛选,类型为字符串
sex: string; sex: string;
// 长者性别,用于筛选,类型为字符串
elderSex: string; elderSex: string;
} }
// 定义一个接口 IGetElderById用于描述根据编号获取长者信息时的请求参数结构
interface IGetElderById { interface IGetElderById {
// 长者 ID类型为字符串
elderId: string; elderId: string;
} }
// 定义一个接口 IEditElder用于描述编辑长者信息时的数据结构
interface IEditElder { interface IEditElder {
// 长者 ID类型为数字
id: number; id: number;
// 姓名,类型为字符串
name: string; name: string;
// 身份证号码,类型为字符串
idNum: string; idNum: string;
// 年龄,类型为数字
age: number; age: number;
// 性别,类型为字符串
sex: string; sex: string;
// 电话号码,类型为字符串
phone: string; phone: string;
// 地址,类型为字符串
address: string; address: string;
} }
// 性别 // 定义一个名为 sexList 的常量,是一个包含性别选项的数组,每个选项有标签和对应的值
export const sexList = [ export const sexList = [
{ label: "男", value: "男" }, { label: "男", value: "男" },
{ label: "女", value: "女" } { label: "女", value: "女" }
]; ];
// 导出excel /**
* exportExcel Excel
* @returns Promise Excel
*/
export function exportExcel() { export function exportExcel() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/elderRecord/exportExcel"
return http.get("/api/elderRecord/exportExcel"); return http.get("/api/elderRecord/exportExcel");
} }
// 分页查询员工 /**
* pageElderByKey
* @param data - IPageElderByKey
* @returns Promise
*/
export async function pageElderByKey(data: IPageElderByKey) { export async function pageElderByKey(data: IPageElderByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,但是组件封装是需要一样的,所以请求前增加一些这两个字段
Reflect.has(data, "sex") ? (data.elderSex = data.sex) : ""; // 如果 data 对象中有 "sex" 字段,则将其值赋给 "elderSex" 字段
Reflect.has(data, "name") ? (data.elderName = data.name) : ""; Reflect.has(data, "sex")? (data.elderSex = data.sex) : "";
// 如果 data 对象中有 "name" 字段,则将其值赋给 "elderName" 字段
Reflect.has(data, "name")? (data.elderName = data.name) : "";
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/elderRecord/pageElderByKey"
// 将处理后的 data 对象展开作为请求的参数
return http.get("/api/elderRecord/pageElderByKey", { return http.get("/api/elderRecord/pageElderByKey", {
params: { params: {
...data ...data
@ -45,8 +74,14 @@ export async function pageElderByKey(data: IPageElderByKey) {
}); });
} }
// 根据编号获取长者信息 /**
* getElderById
* @param data - IGetElderById
* @returns Promise
*/
export async function getElderById(data: IGetElderById) { export async function getElderById(data: IGetElderById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/elderRecord/getElderById"
// 将 data 对象展开作为请求的参数
return http.get("/api/elderRecord/getElderById", { return http.get("/api/elderRecord/getElderById", {
params: { params: {
...data ...data
@ -54,8 +89,14 @@ export async function getElderById(data: IGetElderById) {
}); });
} }
// 根据编号获取长者档案 /**
* getElderRecordById
* @param data - IGetElderById
* @returns Promise
*/
export async function getElderRecordById(data: IGetElderById) { export async function getElderRecordById(data: IGetElderById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/elderRecord/getElderRecordById"
// 将 data 对象展开作为请求的参数
return http.get("/api/elderRecord/getElderRecordById", { return http.get("/api/elderRecord/getElderRecordById", {
params: { params: {
...data ...data
@ -63,13 +104,25 @@ export async function getElderRecordById(data: IGetElderById) {
}); });
} }
// 编辑长者 /**
* editElder
* @param data - IEditElder
* @returns Promise
*/
export function editElder(data: IEditElder) { export function editElder(data: IEditElder) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/elderRecord/editElder"
// 将 data 对象作为请求的主体
return http.put("/api/elderRecord/editElder", data); return http.put("/api/elderRecord/editElder", data);
} }
// 删除长者 /**
* deleteElder
* @param data - IGetElderById
* @returns Promise
*/
export async function deleteElder(data: IGetElderById) { export async function deleteElder(data: IGetElderById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/elderRecord/deleteElder"
// 将 data 对象展开作为请求的参数
return http.delete("/api/elderRecord/deleteElder", { return http.delete("/api/elderRecord/deleteElder", {
params: { params: {
...data ...data

@ -1,25 +1,56 @@
// 从 '@/utils' 模块导入名为 http 的对象,该对象可能是用于发起 HTTP 请求的工具,比如对 axios 等请求库的封装
import { http } from '@/utils' import { http } from '@/utils'
// 可售床位
/**
* getAvailableBed
* @returns Promise
*/
export async function getAvailableBed() { export async function getAvailableBed() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/home/availableBed'
return http.get('/api/home/availableBed') return http.get('/api/home/availableBed')
} }
// 业务趋势
/**
* getBusinessTrend
* @returns Promise
*/
export async function getBusinessTrend() { export async function getBusinessTrend() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/home/businessTrend'
return http.get('/api/home/businessTrend') return http.get('/api/home/businessTrend')
} }
// 客户来源渠道
/**
* getClientSource
* @returns Promise
*/
export async function getClientSource() { export async function getClientSource() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/home/clientSource'
return http.get('/api/home/clientSource') return http.get('/api/home/clientSource')
} }
// 本月业绩排行
/**
* getMonthPerformanceRank
* @returns Promise
*/
export async function getMonthPerformanceRank() { export async function getMonthPerformanceRank() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/home/monthPerformanceRank'
return http.get('/api/home/monthPerformanceRank') return http.get('/api/home/monthPerformanceRank')
} }
// 今日概览
/**
* getTodayOverview
* @returns Promise
*/
export async function getTodayOverview() { export async function getTodayOverview() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/home/todayOverview'
return http.get('/api/home/todayOverview') return http.get('/api/home/todayOverview')
} }
// 今日销售跟进
/**
* getTodaySaleFollow
* @returns Promise
*/
export async function getTodaySaleFollow() { export async function getTodaySaleFollow() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/home/todaySaleFollow'
return http.get('/api/home/todaySaleFollow') return http.get('/api/home/todaySaleFollow')
} }

@ -1,16 +1,29 @@
// 注释说明这是动态路由的假数据,并且提示该部分代码可以删除
// 动态路由的假数据 可删除 // 动态路由的假数据 可删除
// 从 '@/utils' 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from '@/utils' import { http } from '@/utils'
// 注释说明发起请求的时机在动态设置路由的时候data => 树形结构 => 路由列表)
// 问题:何时发起请求? 在动态设置路由的时候data => 树形结构 => 路由列表) // 问题:何时发起请求? 在动态设置路由的时候data => 树形结构 => 路由列表)
/**
* getUserRouteList
* @param uid - ID
* @returns Promise
*/
function getUserRouteList(uid: number) { function getUserRouteList(uid: number) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 '/api/user_router_list',请求体包含用户 ID
return http return http
.post('/api/user_router_list', { uid }) .post('/api/user_router_list', { uid })
// 处理请求成功的情况,直接返回请求得到的数据
.then((data) => data) .then((data) => data)
// 处理请求失败的情况,捕获错误并重新抛出,以便上层调用者处理
.catch((err) => { .catch((err) => {
throw err throw err
}) })
} }
// 导出 getUserRouteList 函数,以便在其他模块中可以导入和使用该函数
export { getUserRouteList } export { getUserRouteList }

@ -1,37 +1,64 @@
// 从 "@/utils" 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/service" 模块导入 IPageServiceByKey 接口,
// 用于描述分页查询服务时的请求参数结构
import { IPageServiceByKey } from "@/apis/service"; import { IPageServiceByKey } from "@/apis/service";
// 定义一个接口 IPageNurseGradeByKey用于描述分页查询护理等级时的请求参数结构
interface IPageNurseGradeByKey { interface IPageNurseGradeByKey {
name:string; // 名称,用于筛选护理等级,类型为字符串
name: string;
// 护理等级名称,用于筛选护理等级,类型为字符串
gradeName: string; gradeName: string;
type:string; // 类型,用于筛选护理等级,类型为字符串
type: string;
// 护理类型,用于筛选护理等级,类型为字符串
nurseType: string; nurseType: string;
} }
// 定义一个接口 IAddNurseGrade用于描述新增护理等级时的数据结构
interface IAddNurseGrade { interface IAddNurseGrade {
// 护理等级的 ID类型为数字
id: number; id: number;
// 护理等级的名称,类型为字符串
name: string; name: string;
// 护理等级的类型,类型为字符串
type: string; type: string;
// 护理等级的月价格,类型为数字
monthPrice: number; monthPrice: number;
serviceIdList: any ; // 服务 ID 列表,类型为 any需根据实际情况调整
serviceIdList: any;
} }
// 定义一个接口 IGetNurseGradeById用于描述根据编号查询护理等级时的请求参数结构
interface IGetNurseGradeById { interface IGetNurseGradeById {
nurseGradeId: string // 护理等级 ID类型为字符串
nurseGradeId: string;
} }
// 护理类型 // 定义一个名为 INurseTypeList 的常量,是一个包含护理类型选项的数组,每个选项有标签和对应的值
export const INurseTypeList = [ export const INurseTypeList = [
{ label: "自理", value: "自理" }, { label: "自理", value: "自理" },
{ label: "介护", value: "介护" }, { label: "介护", value: "介护" },
{ label: "全护", value: "全护" } { label: "全护", value: "全护" }
]; ];
// 分页查询护理等级 /**
* pageNurseGradeByKey
* @param data - IPageNurseGradeByKey
* @returns Promise
*/
export async function pageNurseGradeByKey(data: IPageNurseGradeByKey) { export async function pageNurseGradeByKey(data: IPageNurseGradeByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
Reflect.has(data, 'name') ? (data.gradeName = data.name) : '' // 但是组件封装需要一样,所以请求前增加一些这两个字段
Reflect.has(data, 'type') ? (data.nurseType = data.type) : '' // 如果 data 对象中有 'name' 字段,则将其值赋给 'gradeName' 字段
Reflect.has(data, 'name')? (data.gradeName = data.name) : '';
// 如果 data 对象中有 'type' 字段,则将其值赋给 'nurseType' 字段
Reflect.has(data, 'type')? (data.nurseType = data.type) : '';
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseGrade/pageNurseGradeByKey"
// 将处理后的 data 对象展开作为请求的参数
return http.get("/api/nurseGrade/pageNurseGradeByKey", { return http.get("/api/nurseGrade/pageNurseGradeByKey", {
params: { params: {
...data ...data
@ -39,13 +66,23 @@ export async function pageNurseGradeByKey(data: IPageNurseGradeByKey) {
}); });
} }
// 获取服务类型 /**
* listServiceType
* @returns Promise
*/
export function listServiceType() { export function listServiceType() {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/nurseGrade/listServiceType"
return http.post("/api/nurseGrade/listServiceType"); return http.post("/api/nurseGrade/listServiceType");
} }
// 分页查询服务 /**
* pageServiceByKey
* @param data - IPageServiceByKey
* @returns Promise
*/
export async function pageServiceByKey(data: IPageServiceByKey) { export async function pageServiceByKey(data: IPageServiceByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseGrade/pageServiceByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/nurseGrade/pageServiceByKey", { return http.get("/api/nurseGrade/pageServiceByKey", {
params: { params: {
...data ...data
@ -53,13 +90,25 @@ export async function pageServiceByKey(data: IPageServiceByKey) {
}); });
} }
// 新增护理等级 /**
* addNurseGrade
* @param data - IAddNurseGrade
* @returns Promise
*/
export function addNurseGrade(data: IAddNurseGrade) { export function addNurseGrade(data: IAddNurseGrade) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/nurseGrade/addNurseGrade"
// 将 data 对象作为请求的主体
return http.post("/api/nurseGrade/addNurseGrade", data); return http.post("/api/nurseGrade/addNurseGrade", data);
} }
// 根据编号查询护理等级 /**
* getNurseGradeById
* @param data - IGetNurseGradeById
* @returns Promise
*/
export async function getNurseGradeById(data: IGetNurseGradeById) { export async function getNurseGradeById(data: IGetNurseGradeById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseGrade/getNurseGradeById"
// 将 data 对象展开作为请求的参数
return http.get("/api/nurseGrade/getNurseGradeById", { return http.get("/api/nurseGrade/getNurseGradeById", {
params: { params: {
...data ...data
@ -67,13 +116,25 @@ export async function getNurseGradeById(data: IGetNurseGradeById) {
}); });
} }
// 编辑护理等级 /**
* editNurseGrade
* @param data - IAddNurseGrade
* @returns Promise
*/
export function editNurseGrade(data: IAddNurseGrade) { export function editNurseGrade(data: IAddNurseGrade) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/nurseGrade/editNurseGrade"
// 将 data 对象作为请求的主体
return http.put("/api/nurseGrade/editNurseGrade", data); return http.put("/api/nurseGrade/editNurseGrade", data);
} }
// 删除护理等级 /**
* deleteNurseGrade
* @param data - IGetNurseGradeById
* @returns Promise
*/
export async function deleteNurseGrade(data: IGetNurseGradeById) { export async function deleteNurseGrade(data: IGetNurseGradeById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/nurseGrade/deleteNurseGrade"
// 将 data 对象展开作为请求的参数
return http.delete("/api/nurseGrade/deleteNurseGrade", { return http.delete("/api/nurseGrade/deleteNurseGrade", {
params: { params: {
...data ...data

@ -1,34 +1,64 @@
// 从 "@/utils" 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,
// 用于描述分页搜索老人时的请求参数结构
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 从 "@/apis/service" 模块导入 IGetServiceById 接口,
// 用于描述根据编号查询服务时的请求参数结构
import { IGetServiceById } from "@/apis/service"; import { IGetServiceById } from "@/apis/service";
// 定义一个接口 IPageNurseReserveByKey用于描述分页查询护理预定时的请求参数结构
interface IPageNurseReserveByKey { interface IPageNurseReserveByKey {
// 床位名称,用于筛选护理预定记录,类型为字符串
bedName: string; bedName: string;
elderName:string; // 老人姓名,用于筛选护理预定记录,类型为字符串
elderName: string;
// 服务名称,用于筛选护理预定记录,类型为字符串
serviceName: string; serviceName: string;
} }
// 定义一个接口 IAddNurseReserve用于描述新增护理预定时的数据结构
interface IAddNurseReserve { interface IAddNurseReserve {
// 老人 ID类型为数字
elderId: number; elderId: number;
// 服务名称,类型为字符串
serviceName: string; serviceName: string;
// 需要服务的日期(这里推测为时间戳等能表示日期的值,类型为数字)
needDate: number; needDate: number;
// 服务价格,类型为数字
servicePrice: number; servicePrice: number;
// 收费方式,类型为字符串
chargeMethod: string; chargeMethod: string;
// 频率,类型为数字
frequency: number; frequency: number;
// 支付金额,类型为数字
payAmount: number; payAmount: number;
} }
// 定义一个接口 IExecuteNurseReserve用于描述执行护理预定时的数据结构
interface IExecuteNurseReserve { interface IExecuteNurseReserve {
// 护理预定的 ID类型为数字
id: number; id: number;
// 护理日期,类型为字符串
nurseDate: string; nurseDate: string;
// 护理人员 ID类型为字符串
staffId: string; staffId: string;
} }
// 分页查询护理预定 /**
* pageNurseReserveByKey
* @param data - IPageNurseReserveByKey
* @returns Promise
*/
export async function pageNurseReserveByKey(data: IPageNurseReserveByKey) { export async function pageNurseReserveByKey(data: IPageNurseReserveByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
// Reflect.has(data, 'name') ? (data.gradeName = data.name) : '' // 但是组件封装需要一样,所以原本计划请求前处理某些字段,
// Reflect.has(data, 'type') ? (data.nurseType = data.type) : '' // 但这里代码被注释掉了,可能是暂不处理或有其他调整
// Reflect.has(data, 'name')? (data.gradeName = data.name) : ''
// Reflect.has(data, 'type')? (data.nurseType = data.type) : ''
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseReserve/pageNurseReserveByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/nurseReserve/pageNurseReserveByKey", { return http.get("/api/nurseReserve/pageNurseReserveByKey", {
params: { params: {
...data ...data
@ -36,27 +66,49 @@ export async function pageNurseReserveByKey(data: IPageNurseReserveByKey) {
}); });
} }
// 分页搜索老人 /**
* pageSearchElderByKey
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export function pageSearchElderByKey(data: IPageSearchElderByKey) { export function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseReserve/pageSearchElderByKey"
// 将 data 对象展开作为请求的参数
return http.get('/api/nurseReserve/pageSearchElderByKey', { return http.get('/api/nurseReserve/pageSearchElderByKey', {
params: { params: {
...data ...data
} }
}) });
} }
// 获取服务项目 /**
* listService
* @returns Promise
*/
export async function listService() { export async function listService() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseReserve/listService"
return http.get("/api/nurseReserve/listService"); return http.get("/api/nurseReserve/listService");
} }
// 新增护理预定 /**
* addNurseReserve
* @param data - IAddNurseReserve
* @returns Promise
*/
export function addNurseReserve(data: IAddNurseReserve) { export function addNurseReserve(data: IAddNurseReserve) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/nurseReserve/addNurseReserve"
// 将 data 对象作为请求的主体
return http.post("/api/nurseReserve/addNurseReserve", data); return http.post("/api/nurseReserve/addNurseReserve", data);
} }
// 根据编号查询护理预定 /**
* getServiceById
* @param data - IGetServiceById
* @returns Promise
*/
export async function getServiceById(data: IGetServiceById) { export async function getServiceById(data: IGetServiceById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseReserve/getServiceById"
// 将 data 对象展开作为请求的参数
return http.get("/api/nurseReserve/getServiceById", { return http.get("/api/nurseReserve/getServiceById", {
params: { params: {
...data ...data
@ -64,12 +116,22 @@ export async function getServiceById(data: IGetServiceById) {
}); });
} }
// 护理人员 /**
* listNurseStaff
* @returns Promise
*/
export async function listNurseStaff() { export async function listNurseStaff() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/nurseReserve/listNurseStaff"
return http.get("/api/nurseReserve/listNurseStaff"); return http.get("/api/nurseReserve/listNurseStaff");
} }
// 执行护理预定 /**
* executeNurseReserve
* @param data - IExecuteNurseReserve
* @returns Promise
*/
export function executeNurseReserve(data: IExecuteNurseReserve) { export function executeNurseReserve(data: IExecuteNurseReserve) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/nurseReserve/executeNurseReserve"
// 将 data 对象作为请求的主体
return http.put("/api/nurseReserve/executeNurseReserve", data); return http.put("/api/nurseReserve/executeNurseReserve", data);
} }

@ -1,40 +1,70 @@
// 从 "@/utils" 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,
// 用于描述分页搜索老人时的请求参数结构
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 从 "@/apis/dishes" 模块导入 IPageDishesByKey 接口,
// 用于描述分页查询菜品时的请求参数结构
import { IPageDishesByKey } from "@/apis/dishes"; import { IPageDishesByKey } from "@/apis/dishes";
// 定义一个接口 IPageOrderByKey用于描述分页查询点餐时的请求参数结构
interface IPageOrderByKey { interface IPageOrderByKey {
elderName:string; // 老人姓名,用于筛选点餐记录,类型为字符串
elderName: string;
// 老人电话,用于筛选点餐记录,类型为字符串
elderPhone: string; elderPhone: string;
} }
// 定义一个接口 IAddOrder用于描述新增点餐时的数据结构
interface IAddOrder { interface IAddOrder {
// 老人 ID类型为字符串
elderId: string; elderId: string;
// 就餐方式,类型为字符串
dineType: string; dineType: string;
// 就餐日期,类型为字符串
dineDate: string; dineDate: string;
// 点的菜品列表,类型为 any需根据实际情况调整
orderDishesList: any; orderDishesList: any;
} }
// 定义一个接口 IGetOrderById用于描述根据编号查询点餐时的请求参数结构
interface IGetOrderById { interface IGetOrderById {
// 菜品 ID这里可能是点餐记录中的菜品 ID用于查询特定点餐记录类型为字符串
dishesId: string; dishesId: string;
} }
// 定义一个接口 ISendOrder用于描述送餐时的数据结构
interface ISendOrder { interface ISendOrder {
// 订单 ID类型为字符串
id: string; id: string;
// 送餐日期,类型为字符串
deliverDishesDate: string; deliverDishesDate: string;
// 送餐人员 ID类型为字符串
staffId: string; staffId: string;
} }
// 就餐方式 // 定义一个名为 IDineTypeList 的常量,是一个包含就餐方式选项的数组,每个选项有标签和对应的值
export const IDineTypeList = [ export const IDineTypeList = [
{ label: "送餐", value: "送餐" }, { label: "送餐", value: "送餐" },
{ label: "堂食", value: "堂食" } { label: "堂食", value: "堂食" }
]; ];
// 分页查询点餐 /**
* pageOrderByKey
* @param data - IPageOrderByKey
* @returns Promise
*/
export async function pageOrderByKey(data: IPageOrderByKey) { export async function pageOrderByKey(data: IPageOrderByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
// Reflect.has(data, 'name') ? (data.gradeName = data.name) : '' // 但是组件封装需要一样,所以原本计划请求前处理某些字段,
// Reflect.has(data, 'type') ? (data.nurseType = data.type) : '' // 但这里代码被注释掉了,可能是暂不处理或有其他调整
// Reflect.has(data, 'name')? (data.gradeName = data.name) : ''
// Reflect.has(data, 'type')? (data.nurseType = data.type) : ''
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/order/pageOrderByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/order/pageOrderByKey", { return http.get("/api/order/pageOrderByKey", {
params: { params: {
...data ...data
@ -42,31 +72,55 @@ export async function pageOrderByKey(data: IPageOrderByKey) {
}); });
} }
// 分页搜索老人 /**
* pageSearchElderByKey
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export function pageSearchElderByKey(data: IPageSearchElderByKey) { export function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/order/pageSearchElderByKey"
// 将 data 对象展开作为请求的参数
return http.get('/api/order/pageSearchElderByKey', { return http.get('/api/order/pageSearchElderByKey', {
params: { params: {
...data ...data
} }
}) });
} }
// 分页查询菜品 /**
* pageDishesByKey
* @param data - IPageDishesByKey
* @returns Promise
*/
export async function pageDishesByKey(data: IPageDishesByKey) { export async function pageDishesByKey(data: IPageDishesByKey) {
return http.get("/api/order/pageDishesByKey",{ // 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/order/pageDishesByKey"
params:{ // 将 data 对象展开作为请求的参数
return http.get("/api/order/pageDishesByKey", {
params: {
...data ...data
} }
}); });
} }
// 新增点餐 /**
* addOrder
* @param data - IAddOrder
* @returns Promise
*/
export function addOrder(data: IAddOrder) { export function addOrder(data: IAddOrder) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/order/addOrder"
// 将 data 对象作为请求的主体
return http.post("/api/order/addOrder", data); return http.post("/api/order/addOrder", data);
} }
// 根据编号查询点餐 /**
* getOrderById
* @param data - IGetOrderById
* @returns Promise
*/
export async function getOrderById(data: IGetOrderById) { export async function getOrderById(data: IGetOrderById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/order/getOrderById"
// 将 data 对象展开作为请求的参数
return http.get("/api/order/getOrderById", { return http.get("/api/order/getOrderById", {
params: { params: {
...data ...data
@ -74,12 +128,22 @@ export async function getOrderById(data: IGetOrderById) {
}); });
} }
// 护理人员 /**
* listNurseStaff
* @returns Promise
*/
export async function listNurseStaff() { export async function listNurseStaff() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/order/listNurseStaff"
return http.get("/api/order/listNurseStaff"); return http.get("/api/order/listNurseStaff");
} }
// 送餐 /**
* sendOrder
* @param data - ISendOrder
* @returns Promise
*/
export function sendOrder(data: ISendOrder) { export function sendOrder(data: ISendOrder) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/order/sendOrder"
// 将 data 对象作为请求的主体
return http.put("/api/order/sendOrder", data); return http.put("/api/order/sendOrder", data);
} }

@ -1,48 +1,77 @@
// 从 "@/utils" 模块导入名为 http 的对象,该对象可用于发起 HTTP 请求,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,此接口用于定义分页搜索老人时的请求参数结构
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 定义 IPageOutwardByKey 接口,用于描述分页查询外出登记时的请求参数结构
interface IPageOutwardByKey { interface IPageOutwardByKey {
// 老人姓名,用于筛选外出登记记录,类型为字符串
elderName: string; elderName: string;
// 陪同人类型,用于筛选外出登记记录,类型为字符串
chaperoneType: string; chaperoneType: string;
// 外出开始时间,用于筛选外出登记记录,类型为字符串
startTime: string; startTime: string;
// 外出结束时间,用于筛选外出登记记录,类型为字符串
endTime: string; endTime: string;
} }
// 定义 IListContactByElderId 接口,用于描述根据老人 ID 获取紧急联系人列表时的请求参数结构
interface IListContactByElderId { interface IListContactByElderId {
// 老人 ID类型为字符串
elderId: string; elderId: string;
} }
// 定义 IAddOutward 接口,用于描述新增外出登记时的数据结构
interface IAddOutward { interface IAddOutward {
// 老人 ID类型为数字
elderId: number; elderId: number;
// 陪同人姓名,类型为字符串
chaperoneName: string; chaperoneName: string;
// 陪同人电话,类型为字符串
chaperonePhone: string; chaperonePhone: string;
// 陪同人类型,类型为字符串
chaperoneType: string; chaperoneType: string;
// 外出日期,类型为字符串
outwardDate: string; outwardDate: string;
// 计划返回日期,类型为字符串
planReturnDate: string; planReturnDate: string;
} }
// 定义 IGetOutwardById 接口,用于描述根据编号获取外出登记时的请求参数结构
interface IGetOutwardById { interface IGetOutwardById {
// 外出登记 ID类型为字符串
outwardId: string; outwardId: string;
} }
// 定义 IDelayReturn 接口,用于描述延期返回操作时的数据结构
interface IDelayReturn { interface IDelayReturn {
// 外出登记 ID类型为字符串
id: string; id: string;
// 新的计划返回日期,类型为字符串
planReturnDate: string; planReturnDate: string;
} }
// 定义 IRecordReturn 接口,用于描述登记返回操作时的数据结构
interface IRecordReturn { interface IRecordReturn {
// 外出登记 ID类型为字符串
id: string; id: string;
// 实际返回日期,类型为 any具体类型需根据实际情况确定
realReturnDate: any; realReturnDate: any;
} }
// 陪同人类型 // 定义一个常量 typeList包含陪同人类型的选项,每个选项有标签和对应的值
export const typeList = [ export const typeList = [
{ label: "护工", value: "护工" }, { label: "护工", value: "护工" },
{ label: "家属", value: "家属" } { label: "家属", value: "家属" }
]; ];
// 分页查询外出登记 /**
* pageOutwardByKey
* @param data - IPageOutwardByKey
* @returns Promise
*/
export async function pageOutwardByKey(data: IPageOutwardByKey) { export async function pageOutwardByKey(data: IPageOutwardByKey) {
// 使用 http 对象发起 GET 请求,请求地址为 "/api/outward/pageOutwardByKey",并将 data 对象展开作为请求参数
return http.get("/api/outward/pageOutwardByKey", { return http.get("/api/outward/pageOutwardByKey", {
params: { params: {
...data ...data
@ -50,8 +79,13 @@ export async function pageOutwardByKey(data: IPageOutwardByKey) {
}); });
} }
// 分页搜索老人 /**
* pageSearchElderByKey
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export async function pageSearchElderByKey(data: IPageSearchElderByKey) { export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用 http 对象发起 GET 请求,请求地址为 "/api/outward/pageSearchElderByKey",并将 data 对象展开作为请求参数
return http.get("/api/outward/pageSearchElderByKey", { return http.get("/api/outward/pageSearchElderByKey", {
params: { params: {
...data ...data
@ -59,13 +93,22 @@ export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
}); });
} }
// 获取护工列表 /**
* listOutwardStaff
* @returns Promise
*/
export async function listOutwardStaff() { export async function listOutwardStaff() {
// 使用 http 对象发起 GET 请求,请求地址为 "/api/outward/listOutwardStaff"
return http.get("/api/outward/listOutwardStaff"); return http.get("/api/outward/listOutwardStaff");
} }
// 获取紧急联系人列表 /**
* listContactByElderId ID
* @param data - IListContactByElderId ID
* @returns Promise
*/
export async function listContactByElderId(data: IListContactByElderId) { export async function listContactByElderId(data: IListContactByElderId) {
// 使用 http 对象发起 GET 请求,请求地址为 "/api/outward/listContactByElderId",并将 data 对象展开作为请求参数
return http.get("/api/outward/listContactByElderId", { return http.get("/api/outward/listContactByElderId", {
params: { params: {
...data ...data
@ -73,13 +116,23 @@ export async function listContactByElderId(data: IListContactByElderId) {
}); });
} }
// 新增外出登记 /**
* addOutward
* @param data - IAddOutward
* @returns Promise
*/
export function addOutward(data: IAddOutward) { export function addOutward(data: IAddOutward) {
// 使用 http 对象发起 POST 请求,请求地址为 "/api/outward/addOutward",并将 data 对象作为请求体
return http.post("/api/outward/addOutward", data); return http.post("/api/outward/addOutward", data);
} }
// 根据编号获取外出登记 /**
* getOutwardById
* @param data - IGetOutwardById ID
* @returns Promise
*/
export async function getOutwardById(data: IGetOutwardById) { export async function getOutwardById(data: IGetOutwardById) {
// 使用 http 对象发起 GET 请求,请求地址为 "/api/outward/getOutwardById",并将 data 对象展开作为请求参数
return http.get("/api/outward/getOutwardById", { return http.get("/api/outward/getOutwardById", {
params: { params: {
...data ...data
@ -87,18 +140,33 @@ export async function getOutwardById(data: IGetOutwardById) {
}); });
} }
// 延期返回 /**
* delayReturn
* @param data - IDelayReturn
* @returns Promise
*/
export function delayReturn(data: IDelayReturn) { export function delayReturn(data: IDelayReturn) {
// 使用 http 对象发起 PUT 请求,请求地址为 "/api/outward/delayReturn",并将 data 对象作为请求体
return http.put("/api/outward/delayReturn", data); return http.put("/api/outward/delayReturn", data);
} }
// 登记返回 /**
* recordReturn
* @param data - IRecordReturn
* @returns Promise
*/
export function recordReturn(data: IRecordReturn) { export function recordReturn(data: IRecordReturn) {
// 使用 http 对象发起 PUT 请求,请求地址为 "/api/outward/recordReturn",并将 data 对象作为请求体
return http.put("/api/outward/recordReturn", data); return http.put("/api/outward/recordReturn", data);
} }
// 删除外出登记 /**
* deleteOutward
* @param data - IGetOutwardById ID
* @returns Promise
*/
export async function deleteOutward(data: IGetOutwardById) { export async function deleteOutward(data: IGetOutwardById) {
// 使用 http 对象发起 DELETE 请求,请求地址为 "/api/outward/deleteOutward",并将 data 对象展开作为请求参数
return http.delete("/api/outward/deleteOutward", { return http.delete("/api/outward/deleteOutward", {
params: { params: {
...data ...data

@ -1,19 +1,37 @@
// 从 "@/utils" 模块导入名为 http 的对象,
// 推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,
// 用于描述分页搜索老人时的请求参数结构
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 定义一个接口 IPageRetreatApplyByKey用于描述分页查询退住申请时的请求参数结构
interface IPageRetreatApplyByKey { interface IPageRetreatApplyByKey {
// 床位名称,用于筛选退住申请记录,类型为字符串
bedName: string; bedName: string;
// 老人姓名,用于筛选退住申请记录,类型为字符串
elderName: string; elderName: string;
// 老人性别,用于筛选退住申请记录,类型为字符串
elderSex: string; elderSex: string;
// 身份证号码,用于筛选退住申请记录,类型为字符串
idNum: string; idNum: string;
} }
// 定义一个接口 IAddRetreatApply用于描述新增退住申请时的数据结构
interface IAddRetreatApply { interface IAddRetreatApply {
// 老人 ID类型为数字用于标识发起退住申请的老人
elderId: number; elderId: number;
} }
// 分页查询退住申请 /**
* pageRetreatApplyByKey退
* @param data - IPageRetreatApplyByKey
* @returns Promise 退
*/
export async function pageRetreatApplyByKey(data: IPageRetreatApplyByKey) { export async function pageRetreatApplyByKey(data: IPageRetreatApplyByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/retreatApply/pageRetreatApplyByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/retreatApply/pageRetreatApplyByKey", { return http.get("/api/retreatApply/pageRetreatApplyByKey", {
params: { params: {
...data ...data
@ -21,8 +39,14 @@ export async function pageRetreatApplyByKey(data: IPageRetreatApplyByKey) {
}); });
} }
// 分页搜索老人 /**
* pageSearchElderByKey
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export async function pageSearchElderByKey(data: IPageSearchElderByKey) { export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/retreatApply/pageSearchElderByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/retreatApply/pageSearchElderByKey", { return http.get("/api/retreatApply/pageSearchElderByKey", {
params: { params: {
...data ...data
@ -30,7 +54,13 @@ export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
}); });
} }
// 新增退住申请 /**
* addRetreatApply退
* @param data - IAddRetreatApply 退
* @returns Promise 退
*/
export function addRetreatApply(data: IAddRetreatApply) { export function addRetreatApply(data: IAddRetreatApply) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/retreatApply/addRetreatApply"
// 将 data 对象作为请求的主体
return http.post("/api/retreatApply/addRetreatApply", data); return http.post("/api/retreatApply/addRetreatApply", data);
} }

@ -1,29 +1,46 @@
// 从 "@/utils" 模块导入名为 http 的对象,该对象可能用于发起 HTTP 请求,例如对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IPageRetreatAuditByKey用于描述分页查询退住审核时的请求参数结构
interface IPageRetreatAuditByKey { interface IPageRetreatAuditByKey {
// 老人姓名,用于筛选退住审核记录,类型为字符串
elderName: string; elderName: string;
// 老人性别,用于筛选退住审核记录,类型为字符串
elderSex: string; elderSex: string;
// 身份证号码,用于筛选退住审核记录,类型为字符串
idNum: string; idNum: string;
} }
// 定义一个接口 IGetElderFeeById用于描述根据编号获取老人费用详情时的请求参数结构
interface IGetElderFeeById { interface IGetElderFeeById {
// 老人 ID类型为数字用于指定获取费用详情的老人
elderId: number; elderId: number;
} }
// 定义一个接口 IAuditElderFee用于描述审核老人费用详情时的数据结构
interface IAuditElderFee { interface IAuditElderFee {
// 申请 ID类型为数字用于标识退住申请
applyId: number; applyId: number;
// 老人 ID类型为数字用于关联老人
elderId: number; elderId: number;
// 审核结果,类型为数字,可能是枚举值(如 0 表示不通过1 表示通过等)
auditResult: number; auditResult: number;
} }
// 审核结果 // 定义一个常量 IAuditResultList包含审核结果的选项,每个选项有标签和对应的值
export const IAuditResultList = [ export const IAuditResultList = [
{ label: "通过", value: "通过" }, { label: "通过", value: "通过" },
{ label: "不通过", value: "不通过" } { label: "不通过", value: "不通过" }
]; ];
// 分页查询退住审核 /**
* pageRetreatAuditByKey退
* @param data - IPageRetreatAuditByKey
* @returns Promise 退
*/
export async function pageRetreatAuditByKey(data: IPageRetreatAuditByKey) { export async function pageRetreatAuditByKey(data: IPageRetreatAuditByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/retreatAudit/pageRetreatAuditByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/retreatAudit/pageRetreatAuditByKey", { return http.get("/api/retreatAudit/pageRetreatAuditByKey", {
params: { params: {
...data ...data
@ -31,8 +48,14 @@ export async function pageRetreatAuditByKey(data: IPageRetreatAuditByKey) {
}); });
} }
// 根据编号获取老人费用详情 /**
* getElderFeeById
* @param data - IGetElderFeeById
* @returns Promise
*/
export async function getElderFeeById(data: IGetElderFeeById) { export async function getElderFeeById(data: IGetElderFeeById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/retreatAudit/getElderFeeById"
// 将 data 对象展开作为请求的参数
return http.get("/api/retreatAudit/getElderFeeById", { return http.get("/api/retreatAudit/getElderFeeById", {
params: { params: {
...data ...data
@ -40,7 +63,13 @@ export async function getElderFeeById(data: IGetElderFeeById) {
}); });
} }
// 审核老人费用详情 /**
* auditElderFee
* @param data - IAuditElderFee
* @returns Promise
*/
export function auditElderFee(data: IAuditElderFee) { export function auditElderFee(data: IAuditElderFee) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/retreatAudit/auditElderFee"
// 将 data 对象作为请求的主体
return http.put("/api/retreatAudit/auditElderFee", data); return http.put("/api/retreatAudit/auditElderFee", data);
} }

@ -1,24 +1,42 @@
// 从 "@/utils" 模块导入名为 http 的对象,该对象可能是用于发起 HTTP 请求的工具,比如对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IPageRoomTypeByKey用于描述分页查询房间类型时的请求参数结构
interface IPageRoomTypeByKey { interface IPageRoomTypeByKey {
// 名称,用于筛选房间类型,类型为字符串
name: string; name: string;
// 房间类型名称,用于筛选房间类型,类型为字符串
roomTypeName: string; roomTypeName: string;
} }
// 定义一个接口 IAddRoomType用于描述新增房间类型时的数据结构
interface IAddRoomType { interface IAddRoomType {
// 房间类型的 ID类型为字符串
id: string; id: string;
// 房间类型的名称,类型为字符串
name: string; name: string;
// 房间类型的月价格,类型为字符串
monthPrice: string; monthPrice: string;
} }
// 定义一个接口 IGetRoomTypeById用于描述根据编号查询房间类型时的请求参数结构
interface IGetRoomTypeById { interface IGetRoomTypeById {
// 房间类型 ID类型为字符串
roomTypeId: string; roomTypeId: string;
} }
// 分页查询房间类型 /**
* pageRoomTypeByKey
* @param data - IPageRoomTypeByKey
* @returns Promise
*/
export async function pageRoomTypeByKey(data: IPageRoomTypeByKey) { export async function pageRoomTypeByKey(data: IPageRoomTypeByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
Reflect.has(data, "name") ? (data.roomTypeName = data.name) : ""; // 但是组件封装需要一样,所以请求前增加一些这两个字段
// 如果 data 对象中有 "name" 字段,则将其值赋给 "roomTypeName" 字段
Reflect.has(data, "name")? (data.roomTypeName = data.name) : "";
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/roomType/pageRoomTypeByKey"
// 将处理后的 data 对象展开作为请求的参数
return http.get("/api/roomType/pageRoomTypeByKey", { return http.get("/api/roomType/pageRoomTypeByKey", {
params: { params: {
...data ...data
@ -26,13 +44,25 @@ export async function pageRoomTypeByKey(data: IPageRoomTypeByKey) {
}); });
} }
// 新增房间类型 /**
* addRoomType
* @param data - IAddRoomType
* @returns Promise
*/
export function addRoomType(data: IAddRoomType) { export function addRoomType(data: IAddRoomType) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/roomType/addRoomType"
// 将 data 对象作为请求的主体
return http.post("/api/roomType/addRoomType", data); return http.post("/api/roomType/addRoomType", data);
} }
// 根据编号查询房间类型 /**
* getRoomTypeById
* @param data - IGetRoomTypeById
* @returns Promise
*/
export async function getRoomTypeById(data: IGetRoomTypeById) { export async function getRoomTypeById(data: IGetRoomTypeById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/roomType/getRoomTypeById"
// 将 data 对象展开作为请求的参数
return http.get("/api/roomType/getRoomTypeById", { return http.get("/api/roomType/getRoomTypeById", {
params: { params: {
...data ...data
@ -40,13 +70,25 @@ export async function getRoomTypeById(data: IGetRoomTypeById) {
}); });
} }
// 编辑房间类型 /**
* editRoomType
* @param data - IAddRoomType
* @returns Promise
*/
export function editRoomType(data: IAddRoomType) { export function editRoomType(data: IAddRoomType) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/roomType/editRoomType"
// 将 data 对象作为请求的主体
return http.put("/api/roomType/editRoomType", data); return http.put("/api/roomType/editRoomType", data);
} }
// 删除房间类型 /**
* deleteRoomType
* @param data - IGetRoomTypeById
* @returns Promise
*/
export async function deleteRoomType(data: IGetRoomTypeById) { export async function deleteRoomType(data: IGetRoomTypeById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/roomType/deleteRoomType"
// 将 data 对象展开作为请求的参数
return http.delete("/api/roomType/deleteRoomType", { return http.delete("/api/roomType/deleteRoomType", {
params: { params: {
...data ...data

@ -1,44 +1,70 @@
// 从 "@/utils" 模块导入名为 http 的对象,推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IGetServiceType用于描述获取服务类型时的请求参数结构
interface IGetServiceType { interface IGetServiceType {
// 服务类型名称,用于筛选服务类型,类型为字符串
serviceTypeName: string; serviceTypeName: string;
} }
// 导出一个接口 IPageServiceByKey用于描述分页查询服务时的请求参数结构
export interface IPageServiceByKey { export interface IPageServiceByKey {
// 名称,用于筛选服务,类型为字符串
name: string; name: string;
// 服务类型 ID用于筛选属于特定类型的服务类型为数字
typeId: number; typeId: number;
} }
// 定义一个接口 IAddServiceType用于描述新增服务类型时的数据结构
interface IAddServiceType { interface IAddServiceType {
// 服务类型的 ID类型为数字
id: number; id: number;
// 服务类型的名称,类型为字符串
name: string; name: string;
} }
// 定义一个接口 IGetServiceTypeById用于描述根据编号获取服务类型时的请求参数结构
interface IGetServiceTypeById { interface IGetServiceTypeById {
// 服务类型 ID类型为字符串
serviceTypeId: string; serviceTypeId: string;
} }
// 定义一个接口 IAddService用于描述新增服务时的数据结构
interface IAddService { interface IAddService {
// 服务的 ID类型为数字
id: number; id: number;
// 服务的名称,类型为字符串
name: string; name: string;
// 服务所需日期,类型为字符串
needDate: string; needDate: string;
// 服务价格,类型为字符串
price: string; price: string;
// 服务所属类型的 ID类型为数字
typeId: number; typeId: number;
// 收费方式,类型为字符串
chargeMethod: string; chargeMethod: string;
} }
// 导出一个接口 IGetServiceById用于描述根据编号获取服务时的请求参数结构
export interface IGetServiceById { export interface IGetServiceById {
// 服务 ID类型为字符串
serviceId: string; serviceId: string;
} }
// 收费方式 // 定义一个名为 IChargeMethodList 的常量,是一个包含收费方式选项的数组,每个选项有标签和对应的值
export const IChargeMethodList = [ export const IChargeMethodList = [
{ label: "按次", value: "按次" }, { label: "按次", value: "按次" },
{ label: "按月", value: "按月" } { label: "按月", value: "按月" }
]; ];
// 获取服务类型列表 /**
* getServiceType
* @param data - IGetServiceType
* @returns Promise
*/
export async function getServiceType(data: IGetServiceType) { export async function getServiceType(data: IGetServiceType) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/service/getServiceType"
// 将 data 对象展开作为请求的参数
return http.get("/api/service/getServiceType", { return http.get("/api/service/getServiceType", {
params: { params: {
...data ...data
@ -46,10 +72,17 @@ export async function getServiceType(data: IGetServiceType) {
}); });
} }
// 分页查询服务 /**
* pageServiceByKey
* @param data - IPageServiceByKey
* @returns Promise
*/
export async function pageServiceByKey(data: IPageServiceByKey) { export async function pageServiceByKey(data: IPageServiceByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,但是组件封装是需要一样的,所以请求前增加一些这两个字段
// Reflect.has(data, 'roleName') ? (data.roleId = data.roleName) : '' // 这里注释掉的代码可能是原本用于处理字段的逻辑,但当前未启用
// Reflect.has(data, 'roleName')? (data.roleId = data.roleName) : ''
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/service/pageServiceByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/service/pageServiceByKey", { return http.get("/api/service/pageServiceByKey", {
params: { params: {
...data ...data
@ -57,13 +90,25 @@ export async function pageServiceByKey(data: IPageServiceByKey) {
}); });
} }
// 新增服务类型 /**
* addServiceType
* @param data - IAddServiceType
* @returns Promise
*/
export function addServiceType(data: IAddServiceType) { export function addServiceType(data: IAddServiceType) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/service/addServiceType"
// 将 data 对象作为请求的主体
return http.post("/api/service/addServiceType", data); return http.post("/api/service/addServiceType", data);
} }
// 根据编号获取服务类型 /**
* getServiceTypeById
* @param data - IGetServiceTypeById
* @returns Promise
*/
export async function getServiceTypeById(data: IGetServiceTypeById) { export async function getServiceTypeById(data: IGetServiceTypeById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/service/getServiceTypeById"
// 将 data 对象展开作为请求的参数
return http.get("/api/service/getServiceTypeById", { return http.get("/api/service/getServiceTypeById", {
params: { params: {
...data ...data
@ -71,13 +116,25 @@ export async function getServiceTypeById(data: IGetServiceTypeById) {
}); });
} }
// 编辑服务类型 /**
* editServiceType
* @param data - IAddServiceType
* @returns Promise
*/
export function editServiceType(data: IAddServiceType) { export function editServiceType(data: IAddServiceType) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/service/editServiceType"
// 将 data 对象作为请求的主体
return http.put("/api/service/editServiceType", data); return http.put("/api/service/editServiceType", data);
} }
// 删除服务类型 /**
* deleteServiceType
* @param data - IGetServiceTypeById
* @returns Promise
*/
export async function deleteServiceType(data: IGetServiceTypeById) { export async function deleteServiceType(data: IGetServiceTypeById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/service/deleteServiceType"
// 将 data 对象展开作为请求的参数
return http.delete("/api/service/deleteServiceType", { return http.delete("/api/service/deleteServiceType", {
params: { params: {
...data ...data
@ -85,13 +142,25 @@ export async function deleteServiceType(data: IGetServiceTypeById) {
}); });
} }
// 新增服务 /**
* addService
* @param data - IAddService
* @returns Promise
*/
export function addService(data: IAddService) { export function addService(data: IAddService) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/service/addService"
// 将 data 对象作为请求的主体
return http.post("/api/service/addService", data); return http.post("/api/service/addService", data);
} }
// 根据编号获取服务 /**
* getServiceById
* @param data - IGetServiceById
* @returns Promise
*/
export async function getServiceById(data: IGetServiceById) { export async function getServiceById(data: IGetServiceById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/service/getServiceById"
// 将 data 对象展开作为请求的参数
return http.get("/api/service/getServiceById", { return http.get("/api/service/getServiceById", {
params: { params: {
...data ...data
@ -99,13 +168,25 @@ export async function getServiceById(data: IGetServiceById) {
}); });
} }
// 编辑服务 /**
* editService
* @param data - IAddService
* @returns Promise
*/
export function editService(data: IAddService) { export function editService(data: IAddService) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/service/editService"
// 将 data 对象作为请求的主体
return http.put("/api/service/editService", data); return http.put("/api/service/editService", data);
} }
// 删除服务 /**
* deleteService
* @param data - IGetServiceById
* @returns Promise
*/
export async function deleteService(data: IGetServiceById) { export async function deleteService(data: IGetServiceById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/service/deleteService"
// 将 data 对象展开作为请求的参数
return http.delete("/api/service/deleteService", { return http.delete("/api/service/deleteService", {
params: { params: {
...data ...data

@ -1,58 +1,110 @@
// 从 '@/utils' 模块导入名为 http 的对象,该对象可能是用于发起 HTTP 请求的工具,比如对 axios 等请求库的封装
import { http } from '@/utils' import { http } from '@/utils'
// 定义一个接口 IAddConsult用于描述新增咨询时的数据结构
interface IAddConsult { interface IAddConsult {
address: string // 地址,类型为字符串
age: string address: string;
consultContent: string // 年龄,类型为字符串
consultDate: string age: string;
consultName: string // 咨询内容,类型为字符串
consultPhone: string consultContent: string;
elderName: string // 咨询日期,类型为字符串
elderPhone: string consultDate: string;
idNum: string // 咨询人姓名,类型为字符串
relation: string consultName: string;
sex: string // 咨询人电话,类型为字符串
sourceId: string | number consultPhone: string;
staffId: string | number // 老人姓名,类型为字符串
consultId?: string | number elderName: string;
elderId?: string | number // 老人电话,类型为字符串
elderPhone: string;
// 身份证号码,类型为字符串
idNum: string;
// 关系,类型为字符串
relation: string;
// 性别,类型为字符串
sex: string;
// 来源 ID可以是字符串或数字
sourceId: string | number;
// 接待人 ID可以是字符串或数字
staffId: string | number;
// 咨询 ID可选类型可以是字符串或数字
consultId?: string | number;
// 老人 ID可选类型可以是字符串或数字
elderId?: string | number;
} }
// 定义一个接口 ISearhFormConsultByKey用于描述分页查询咨询时的请求参数结构
interface ISearhFormConsultByKey { interface ISearhFormConsultByKey {
pageNum: number // 页码,类型为数字
pageSize: number pageNum: number;
consultName?: string // 每页数量,类型为数字
consultPhone?: string pageSize: number;
elderName?: string // 咨询人姓名,可选,类型为字符串
elderPhone?: string consultName?: string;
endTime?: string // 咨询人电话,可选,类型为字符串
sourceId?: string consultPhone?: string;
staffId?: string // 老人姓名,可选,类型为字符串
startTime?: string elderName?: string;
sourceName?: string // 老人电话,可选,类型为字符串
staffName?: string elderPhone?: string;
// 结束时间,可选,类型为字符串
endTime?: string;
// 来源 ID可选类型为字符串
sourceId?: string;
// 接待人 ID可选类型为字符串
staffId?: string;
// 开始时间,可选,类型为字符串
startTime?: string;
// 来源名称,可选,类型为字符串
sourceName?: string;
// 接待人名称,可选,类型为字符串
staffName?: string;
} }
// 定义一个接口 IConsultByForm用于描述根据咨询人编号和老人编号获取咨询信息时的请求参数结构
interface IConsultByForm { interface IConsultByForm {
consultId: string | number // 咨询 ID可以是字符串或数字
elderId: string | number consultId: string | number;
// 老人 ID可以是字符串或数字
elderId: string | number;
} }
// 获取咨询管理表格数据 根据咨询人编号和老人编号获取咨询信息 /**
* getConsultByForm
* @param data - IConsultByForm
* @returns Promise
*/
export async function getConsultByForm(data: IConsultByForm) { export async function getConsultByForm(data: IConsultByForm) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/consult/getConsultByConsultIdAndElderId'
// 将 data 对象展开作为请求的参数
return http.get('/api/consult/getConsultByConsultIdAndElderId', { return http.get('/api/consult/getConsultByConsultIdAndElderId', {
params: { params: {
...data ...data
} }
}) })
} }
//新增资询
/**
* addConsult
* @param data - IAddConsult
* @returns Promise
*/
export function addConsult(data: IAddConsult) { export function addConsult(data: IAddConsult) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 '/api/consult/addConsult'
// 将 data 对象作为请求的主体
return http.post('/api/consult/addConsult', data) return http.post('/api/consult/addConsult', data)
} }
// 删除咨询 /**
* delConsult
* @param elderId - ID
* @returns Promise
*/
export function delConsult(elderId: string | number) { export function delConsult(elderId: string | number) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 '/api/consult/deleteConsult'
// 将 elderId 作为请求的参数
return http.delete('/api/consult/deleteConsult', { return http.delete('/api/consult/deleteConsult', {
params: { params: {
elderId elderId
@ -60,13 +112,25 @@ export function delConsult(elderId: string | number) {
}) })
} }
//编辑咨询 /**
* editConsult
* @param data - IAddConsult
* @returns Promise
*/
export function editConsult(data: IAddConsult) { export function editConsult(data: IAddConsult) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/consult/editConsult'
// 将 data 对象作为请求的主体
return http.put('/api/consult/editConsult', data) return http.put('/api/consult/editConsult', data)
} }
// 转为意向客户 /**
* intentionConsult
* @param elderId - ID
* @returns Promise
*/
export function intentionConsult(elderId: string | number) { export function intentionConsult(elderId: string | number) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/consult/intentionConsult'
// 将包含 elderId 的对象作为请求的主体
return http.put('/api/consult/intentionConsult', { return http.put('/api/consult/intentionConsult', {
data: { data: {
elderId elderId
@ -74,21 +138,37 @@ export function intentionConsult(elderId: string | number) {
}) })
} }
// 来源渠道 /**
* listConsultSource
* @returns Promise
*/
export function listConsultSource() { export function listConsultSource() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/consult/listConsultSource'
return http.get('/api/consult/listConsultSource') return http.get('/api/consult/listConsultSource')
} }
// 接待人 /**
* listConsultStaff
* @returns Promise
*/
export function listConsultStaff() { export function listConsultStaff() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/consult/listConsultStaff'
return http.get('/api/consult/listConsultStaff') return http.get('/api/consult/listConsultStaff')
} }
// 分页查询咨询 /**
* pageConsultByKey
* @param data - ISearhFormConsultByKey
* @returns Promise
*/
export async function pageConsultByKey(data: ISearhFormConsultByKey) { export async function pageConsultByKey(data: ISearhFormConsultByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,但是组件封装是需要一样的,所以请求前增加一些这两个字段
Reflect.has(data, 'sourceName') ? (data.sourceId = data.sourceName) : '' // 如果 data 对象中有'sourceName' 字段,则将其值赋给'sourceId' 字段
Reflect.has(data, 'staffName') ? (data.staffId = data.staffName) : '' Reflect.has(data,'sourceName')? (data.sourceId = data.sourceName) : '';
// 如果 data 对象中有'staffName' 字段,则将其值赋给'staffId' 字段
Reflect.has(data,'staffName')? (data.staffId = data.staffName) : '';
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/consult/pageConsultByKey'
// 将处理后的 data 对象展开作为请求的参数
const res = await http.get('/api/consult/pageConsultByKey', { const res = await http.get('/api/consult/pageConsultByKey', {
params: { params: {
...data ...data
@ -97,17 +177,28 @@ export async function pageConsultByKey(data: ISearhFormConsultByKey) {
return res return res
} }
//意向用户接口 // 定义一个接口 ISearhFormIntentionByKey用于描述分页查询意向客户时的请求参数结构
interface ISearhFormIntentionByKey { interface ISearhFormIntentionByKey {
pageNum: number // 页码,类型为数字
pageSize: number pageNum: number;
elderName?: string // 每页数量,类型为数字
elderPhone?: string | number pageSize: number;
labelId?: number // 老人姓名,可选,类型为字符串
elderName?: string;
// 老人电话,可选,可以是字符串或数字
elderPhone?: string | number;
// 标签 ID可选类型为数字
labelId?: number;
} }
//分页查询意向客户 /**
* pageIntentionByKey
* @param data - ISearhFormIntentionByKey
* @returns Promise
*/
export async function pageIntentionByKey(data: ISearhFormIntentionByKey) { export async function pageIntentionByKey(data: ISearhFormIntentionByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/intention/pageIntentionByKey'
// 将 data 对象展开作为请求的参数
const res = await http.get('/api/intention/pageIntentionByKey', { const res = await http.get('/api/intention/pageIntentionByKey', {
params: { params: {
...data ...data
@ -116,23 +207,47 @@ export async function pageIntentionByKey(data: ISearhFormIntentionByKey) {
return res return res
} }
// 新增沟通记录 /**
* addCommunicationRecord
* @param data - any
* @returns Promise
*/
export function addCommunicationRecord(data: any) { export function addCommunicationRecord(data: any) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 '/api/intention/addCommunicationRecord'
// 将 data 对象作为请求的主体
return http.post('/api/intention/addCommunicationRecord', data) return http.post('/api/intention/addCommunicationRecord', data)
} }
// 新增意向客户 /**
* addIntention
* @param data - any
* @returns Promise
*/
export function addIntention(data: any) { export function addIntention(data: any) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 '/api/intention/addIntention'
// 将 data 对象作为请求的主体
return http.post('/api/intention/addIntention', data) return http.post('/api/intention/addIntention', data)
} }
// 新增回访计划 /**
* addVisitPlan访
* @param data - 访 any
* @returns Promise 访
*/
export function addVisitPlan(data: any) { export function addVisitPlan(data: any) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 '/api/intention/addVisitPlan'
// 将 data 对象作为请求的主体
return http.post('/api/intention/addVisitPlan', data) return http.post('/api/intention/addVisitPlan', data)
} }
// 新增沟通记录 /**
* deleteCommunicationRecord
* @param communicationRecordId - ID any
* @returns Promise
*/
export function deleteCommunicationRecord(communicationRecordId: any) { export function deleteCommunicationRecord(communicationRecordId: any) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 '/api/intention/deleteCommunicationRecord'
// 将 communicationRecordId 作为请求的参数
return http.delete('/api/intention/deleteCommunicationRecord', { return http.delete('/api/intention/deleteCommunicationRecord', {
params: { params: {
communicationRecordId communicationRecordId
@ -140,8 +255,14 @@ export function deleteCommunicationRecord(communicationRecordId: any) {
}) })
} }
// 删除回访计划 /**
* deleteVisitPlan访
* @param visitPlanId - 访 ID any访
* @returns Promise 访
*/
export function deleteVisitPlan(visitPlanId: any) { export function deleteVisitPlan(visitPlanId: any) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 '/api/intention/deleteVisitPlan'
// 将 visitPlanId 作为请求的参数
return http.delete('/api/intention/deleteVisitPlan', { return http.delete('/api/intention/deleteVisitPlan', {
params: { params: {
visitPlanId visitPlanId
@ -149,28 +270,58 @@ export function deleteVisitPlan(visitPlanId: any) {
}) })
} }
// 编辑沟通记录 /**
* editCommunicationRecord
* @param data - any
* @returns Promise
*/
export function editCommunicationRecord(data: any) { export function editCommunicationRecord(data: any) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/intention/editCommunicationRecord'
//将 data 对象作为请求的主体
return http.put('/api/intention/editCommunicationRecord', data) return http.put('/api/intention/editCommunicationRecord', data)
} }
// 编辑意向客户 /**
* editIntention
* @param data - any
* @returns Promise
*/
export function editIntention(data: any) { export function editIntention(data: any) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/intention/editIntention'
// 将 data 对象作为请求的主体
return http.put('/api/intention/editIntention', data) return http.put('/api/intention/editIntention', data)
} }
// 编辑老人标签 /**
* editElderLabel
* @param data - any
* @returns Promise
*/
export function editElderLabel(data: any) { export function editElderLabel(data: any) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/intention/editElderLabel'
// 将 data 对象作为请求的主体
return http.put('/api/intention/editElderLabel', data) return http.put('/api/intention/editElderLabel', data)
} }
// 执行回访计划 /**
* executeVisitPlan访
* @param data - 访 any
* @returns Promise 访
*/
export function executeVisitPlan(data: any) { export function executeVisitPlan(data: any) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/intention/executeVisitPlan'
// 将 data 对象作为请求的主体
return http.put('/api/intention/executeVisitPlan', data) return http.put('/api/intention/executeVisitPlan', data)
} }
// 根据编号获取编辑意向客户标签 /**
* getEditElderLabelById
* @param data - any
* @returns Promise
*/
export function getEditElderLabelById(data: any) { export function getEditElderLabelById(data: any) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/intention/getEditElderLabelById'
// 将 data 对象展开作为请求的参数
return http.get('/api/intention/getEditElderLabelById', { return http.get('/api/intention/getEditElderLabelById', {
params: { params: {
...data ...data
@ -178,8 +329,14 @@ export function getEditElderLabelById(data: any) {
}) })
} }
// 根据编号获取意向客户标签 /**
* getElderLabelById
* @param data - any
* @returns Promise
*/
export function getElderLabelById(data: any) { export function getElderLabelById(data: any) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/intention/getElderLabelById'
// 将 data 对象展开作为请求的参数
return http.get('/api/intention/getElderLabelById', { return http.get('/api/intention/getElderLabelById', {
params: { params: {
...data ...data
@ -187,8 +344,14 @@ export function getElderLabelById(data: any) {
}) })
} }
// 根据编号获取意向客户 /**
* getIntentById
* @param data - any
* @returns Promise
*/
export function getIntentById(data: any) { export function getIntentById(data: any) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/intention/getIntentById'
// 将 data 对象展开作为请求的参数
return http.get('/api/intention/getIntentById', { return http.get('/api/intention/getIntentById', {
params: { params: {
...data ...data
@ -196,8 +359,14 @@ export function getIntentById(data: any) {
}) })
} }
//客户标签 /**
* listLabel
* @param data - any
* @returns Promise
*/
export function listLabel(data: any) { export function listLabel(data: any) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/intention/listLabel'
// 将 data 对象展开作为请求的参数
return http.get('/intention/listLabel', { return http.get('/intention/listLabel', {
params: { params: {
...data ...data
@ -205,29 +374,12 @@ export function listLabel(data: any) {
}) })
} }
// 分页查询沟通记录 /**
* pageCommunicationRecord
* @param data - any
* @returns Promise
*/
export function pageCommunicationRecord(data: any) { export function pageCommunicationRecord(data: any) {
return http.get('/intention/pageCommunicationRecord', { // 使用导入的 http 对象发起 GET 请求,请求地址为 '/intention/pageCommunicationRecord'
params: { // 将 data 对象展开作为请求的参数
...data return http.get
}
})
}
// 分页搜索老人
export function pageSearchElderByKey(data: any) {
return http.get('/intention/pageSearchElderByKey', {
params: {
...data
}
})
}
// 分页查询回访计划
export function pageVisitPlan(data: any) {
return http.get('/intention/pageVisitPlan', {
params: {
...data
}
})
}

@ -1,23 +1,40 @@
// 从 "@/utils" 模块导入名为 http 的对象,推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IPageSourceByKey用于描述分页查询来源渠道时的请求参数结构
interface IPageSourceByKey { interface IPageSourceByKey {
// 名称,用于筛选来源渠道,类型为字符串
name: string; name: string;
// 来源渠道名称,用于筛选来源渠道,类型为字符串
sourceName: string; sourceName: string;
} }
// 定义一个接口 IAddSource用于描述新增来源渠道时的数据结构
interface IAddSource { interface IAddSource {
// 来源渠道的 ID类型为字符串
id: string; id: string;
// 来源渠道的名称,类型为字符串
name: string; name: string;
} }
// 定义一个接口 IGetSourceById用于描述根据编号查询来源渠道时的请求参数结构
interface IGetSourceById { interface IGetSourceById {
// 来源渠道 ID类型为字符串
sourceId: string; sourceId: string;
} }
// 分页查询来源渠道 /**
* pageSourceByKey
* @param data - IPageSourceByKey
* @returns Promise
*/
export async function pageSourceByKey(data: IPageSourceByKey) { export async function pageSourceByKey(data: IPageSourceByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
Reflect.has(data, "name") ? (data.sourceName = data.name) : ""; // 但是组件封装需要一样,所以请求前增加一些这两个字段
// 如果 data 对象中有 "name" 字段,则将其值赋给 "sourceName" 字段
Reflect.has(data, "name")? (data.sourceName = data.name) : "";
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/source/pageSourceByKey"
// 将处理后的 data 对象展开作为请求的参数
return http.get("/api/source/pageSourceByKey", { return http.get("/api/source/pageSourceByKey", {
params: { params: {
...data ...data
@ -25,13 +42,25 @@ export async function pageSourceByKey(data: IPageSourceByKey) {
}); });
} }
// 新增来源渠道 /**
* addSource
* @param data - IAddSource
* @returns Promise
*/
export function addSource(data: IAddSource) { export function addSource(data: IAddSource) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/source/addSource"
// 将 data 对象作为请求的主体
return http.post("/api/source/addSource", data); return http.post("/api/source/addSource", data);
} }
// 根据编号查询来源渠道 /**
* getSourceById
* @param data - IGetSourceById
* @returns Promise
*/
export async function getSourceById(data: IGetSourceById) { export async function getSourceById(data: IGetSourceById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/source/getSourceById"
// 将 data 对象展开作为请求的参数
return http.get("/api/source/getSourceById", { return http.get("/api/source/getSourceById", {
params: { params: {
...data ...data
@ -39,13 +68,25 @@ export async function getSourceById(data: IGetSourceById) {
}); });
} }
// 编辑来源渠道 /**
* editSource
* @param data - IAddSource
* @returns Promise
*/
export function editSource(data: IAddSource) { export function editSource(data: IAddSource) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/source/editSource"
// 将 data 对象作为请求的主体
return http.put("/api/source/editSource", data); return http.put("/api/source/editSource", data);
} }
// 删除来源渠道 /**
* deleteSource
* @param data - IGetSourceById
* @returns Promise
*/
export async function deleteSource(data: IGetSourceById) { export async function deleteSource(data: IGetSourceById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/source/deleteSource"
// 将 data 对象展开作为请求的参数
return http.delete("/api/source/deleteSource", { return http.delete("/api/source/deleteSource", {
params: { params: {
...data ...data

@ -1,38 +1,69 @@
// 从 "@/utils" 模块导入名为 http 的对象,推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 定义一个接口 IPageStaffByKey用于描述分页查询员工时的请求参数结构
interface IPageStaffByKey { interface IPageStaffByKey {
// 角色 ID用于筛选员工类型为数字
roleId: number; roleId: number;
roleName:number; // 这里 roleName 的类型标注为 number 可能有误,通常角色名称应该是字符串类型,用于筛选员工
roleName: number;
// 员工姓名,用于筛选员工,类型为字符串
name: string; name: string;
// 员工电话,用于筛选员工,类型为字符串
phone: string; phone: string;
} }
// 定义一个接口 IAddStaff用于描述新增员工时的数据结构
interface IAddStaff { interface IAddStaff {
// 员工 ID类型为数字
id: number; id: number;
// 员工角色 ID类型为数字
roleId: number; roleId: number;
// 员工姓名,类型为字符串
name: string; name: string;
// 员工身份证号码,类型为字符串
idNum: string; idNum: string;
// 员工年龄,类型为数字
age: number; age: number;
// 员工性别,类型为字符串
sex: string; sex: string;
// 员工电话,类型为字符串
phone: string; phone: string;
// 员工邮箱,类型为字符串
email: string; email: string;
// 员工地址,类型为字符串
address: string; address: string;
// 员工头像地址,类型为字符串
avator: string; avator: string;
} }
// 定义一个接口 IGetStaffById用于描述根据员工编号查询员工时的请求参数结构
interface IGetStaffById { interface IGetStaffById {
staffId: string // 员工 ID类型为字符串
staffId: string;
} }
// 获取角色列表 /**
* getRole
* @returns Promise
*/
export async function getRole() { export async function getRole() {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/staff/getRole"
return http.get("/api/staff/getRole"); return http.get("/api/staff/getRole");
} }
// 分页查询员工 /**
* pageStaffByKey
* @param data - IPageStaffByKey
* @returns Promise
*/
export async function pageStaffByKey(data: IPageStaffByKey) { export async function pageStaffByKey(data: IPageStaffByKey) {
// 因为后台返回的字段与前端表单数据的prop不一样但是组件封装是需要一样的所以请求前增加一些这两个字段 // 因为后台返回的字段与前端表单数据的 prop 不一样,
Reflect.has(data, 'roleName') ? (data.roleId = data.roleName) : '' // 但是组件封装需要一样,所以请求前增加一些这两个字段
// 如果 data 对象中有 'roleName' 字段,则将其值赋给 'roleId' 字段,这里逻辑可能需要进一步确认合理性
Reflect.has(data, 'roleName')? (data.roleId = data.roleName) : '';
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/staff/pageStaffByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/staff/pageStaffByKey", { return http.get("/api/staff/pageStaffByKey", {
params: { params: {
...data ...data
@ -40,13 +71,25 @@ export async function pageStaffByKey(data: IPageStaffByKey) {
}); });
} }
// 新增员工 /**
* addStaff
* @param data - IAddStaff
* @returns Promise
*/
export function addStaff(data: IAddStaff) { export function addStaff(data: IAddStaff) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/staff/addStaff"
// 将 data 对象作为请求的主体
return http.post("/api/staff/addStaff", data); return http.post("/api/staff/addStaff", data);
} }
// 根据老人编号查询员工 /**
* getStaffById
* @param data - IGetStaffById
* @returns Promise
*/
export async function getStaffById(data: IGetStaffById) { export async function getStaffById(data: IGetStaffById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/staff/getStaffById"
// 将 data 对象展开作为请求的参数
return http.get("/api/staff/getStaffById", { return http.get("/api/staff/getStaffById", {
params: { params: {
...data ...data
@ -54,13 +97,25 @@ export async function getStaffById(data: IGetStaffById) {
}); });
} }
// 编辑员工 /**
* editStaff
* @param data - IAddStaff
* @returns Promise
*/
export function editStaff(data: IAddStaff) { export function editStaff(data: IAddStaff) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/staff/editStaff"
// 将 data 对象作为请求的主体
return http.put("/api/staff/editStaff", data); return http.put("/api/staff/editStaff", data);
} }
// 删除员工 /**
* leaveStaff
* @param data - IGetStaffById
* @returns Promise
*/
export async function leaveStaff(data: IGetStaffById) { export async function leaveStaff(data: IGetStaffById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/staff/leaveStaff"
// 将 data 对象展开作为请求的参数
return http.delete("/api/staff/leaveStaff", { return http.delete("/api/staff/leaveStaff", {
params: { params: {
...data ...data

@ -1,60 +1,106 @@
import { http } from '@/utils' // 从 '@/utils' 模块导入名为 http 的对象,推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from '@/utils';
// 定义一个接口 ILoginForm用于描述登录表单的数据结构
interface ILoginForm { interface ILoginForm {
pass: string // 密码,类型为字符串
phone: string pass: string;
// 电话号码,类型为字符串
phone: string;
} }
// 定义一个接口 ISendCodeForm用于描述发送验证码表单的数据结构
interface ISendCodeForm { interface ISendCodeForm {
pass: string // 密码,类型为字符串
account: string pass: string;
// 账号,类型为字符串
account: string;
} }
// 定义一个接口 IForgetPass用于描述忘记密码时的数据结构
interface IForgetPass { interface IForgetPass {
code: string // 验证码,类型为字符串
pass: string code: string;
account: string // 新密码,类型为字符串
pass: string;
// 账号,类型为字符串
account: string;
} }
// 定义一个接口 IEditPass用于描述修改密码时的数据结构
interface IEditPass { interface IEditPass {
newPass: string // 新密码,类型为字符串
oldPass: string newPass: string;
// 旧密码,类型为字符串
oldPass: string;
} }
// 定义一个类 IEditPassImpl实现了 IEditPass 接口,用于表示修改密码的数据实体
export class IEditPassImpl implements IEditPass { export class IEditPassImpl implements IEditPass {
newPass: string // 新密码属性
oldPass: string newPass: string;
// 旧密码属性
oldPass: string;
// 构造函数,接收新密码和旧密码作为参数,并赋值给对应的属性
constructor(newPass: string, oldPass: string) { constructor(newPass: string, oldPass: string) {
this.newPass = newPass this.newPass = newPass;
this.oldPass = oldPass this.oldPass = oldPass;
} }
} }
// 登录 /**
* getLogin
* @param data - ILoginForm
* @returns Promise
*/
export function getLogin(data: ILoginForm) { export function getLogin(data: ILoginForm) {
return http.post('/api/account/login', data) // 使用导入的 http 对象发起 POST 请求,请求地址为 '/api/account/login'
// 将 data 对象作为请求的主体
return http.post('/api/account/login', data);
} }
// 发送验证码 /**
* sendCode
* @param data - ISendCodeForm
* @returns Promise
*/
export async function sendCode(data: ISendCodeForm) { export async function sendCode(data: ISendCodeForm) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 '/api/account/sendCode'
// 将 data 对象展开作为请求的参数
return http.get('/api/account/sendCode', { return http.get('/api/account/sendCode', {
params: { params: {
...data ...data
} }
}) });
} }
// 忘记密码 /**
* forgetPass
* @param data - IForgetPass
* @returns Promise
*/
export async function forgetPass(data: IForgetPass) { export async function forgetPass(data: IForgetPass) {
return http.put('/api/account/forget', data) // 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/account/forget'
// 将 data 对象作为请求的主体
return http.put('/api/account/forget', data);
} }
// 修改密码 /**
* editPass
* @param data - IEditPass
* @returns Promise
*/
export async function editPass(data: IEditPass) { export async function editPass(data: IEditPass) {
return http.put('/api/account/edit', data) // 使用导入的 http 对象发起 PUT 请求,请求地址为 '/api/account/edit'
// 将 data 对象作为请求的主体
return http.put('/api/account/edit', data);
} }
// 退出登录 /**
* getLogout退
* @returns Promise 退
*/
export async function getLogout() { export async function getLogout() {
return http.delete('/api/account/logout') // 使用导入的 http 对象发起 DELETE 请求,请求地址为 '/api/account/logout'
return http.delete('/api/account/logout');
} }

@ -1,49 +1,83 @@
// 从 "@/utils" 模块导入名为 http 的对象,推测该对象是用于发起 HTTP 请求的工具,可能是对 axios 等请求库的封装
import { http } from "@/utils"; import { http } from "@/utils";
// 从 "@/apis/bookManage" 模块导入 IPageSearchElderByKey 接口,
// 用于描述分页搜索老人时的请求参数结构
import { IPageSearchElderByKey } from "@/apis/bookManage"; import { IPageSearchElderByKey } from "@/apis/bookManage";
// 定义一个接口 IPageVisitByKey用于描述分页查询来访登记时的请求参数结构
interface IPageVisitByKey { interface IPageVisitByKey {
// 老人姓名,用于筛选来访登记记录,类型为字符串
elderName: string; elderName: string;
// 来访人姓名,用于筛选来访登记记录,类型为字符串
visitName: string; visitName: string;
// 来访人电话,用于筛选来访登记记录,类型为字符串
visitPhone: string; visitPhone: string;
// 来访状态标识,用于筛选来访登记记录,类型为字符串
visitFlag: string; visitFlag: string;
} }
// 定义一个接口 IAddVisit用于描述新增来访登记时的数据结构
interface IAddVisit { interface IAddVisit {
// 来访登记的 ID类型为数字
id: number; id: number;
// 老人 ID类型为数字
elderId: number; elderId: number;
// 来访人姓名,类型为字符串
name: string; name: string;
// 来访人电话,类型为字符串
phone: string; phone: string;
// 与老人的关系,类型为字符串
relation: string; relation: string;
// 来访日期,类型为字符串
visitDate: string; visitDate: string;
// 来访次数,类型为数字
visitNum: number; visitNum: number;
} }
// 定义一个接口 IGetVisitById用于描述根据编号获取来访登记时的请求参数结构
interface IGetVisitById { interface IGetVisitById {
// 来访登记 ID类型为字符串
visitId: string; visitId: string;
} }
// 定义一个接口 IEditVisit用于描述编辑来访登记时的数据结构
interface IEditVisit { interface IEditVisit {
// 来访登记的 ID类型为数字
id: number; id: number;
// 来访人姓名,类型为字符串
name: string; name: string;
// 来访人电话,类型为字符串
phone: string; phone: string;
// 与老人的关系,类型为字符串
relation: string; relation: string;
// 来访日期字符串,类型为字符串
visitDateStr: string; visitDateStr: string;
// 来访次数,类型为数字
visitNum: number; visitNum: number;
} }
// 定义一个接口 IRecordLeave用于描述登记离开时的数据结构
interface IRecordLeave { interface IRecordLeave {
// 来访登记 ID类型为字符串
id: string; id: string;
// 离开日期,类型为 any具体类型需根据实际情况确定
leaveDate: any; leaveDate: any;
} }
// 来访状态 // 定义一个名为 typeList 的常量,是一个包含来访状态选项的数组,每个选项有标签和对应的值
export const typeList = [ export const typeList = [
{ label: "待离开", value: "待离开" }, { label: "待离开", value: "待离开" },
{ label: "已离开", value: "已离开" } { label: "已离开", value: "已离开" }
]; ];
// 分页查询来访登记 /**
* pageVisitByKey访
* @param data - IPageVisitByKey
* @returns Promise 访
*/
export async function pageVisitByKey(data: IPageVisitByKey) { export async function pageVisitByKey(data: IPageVisitByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/visit/pageVisitByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/visit/pageVisitByKey", { return http.get("/api/visit/pageVisitByKey", {
params: { params: {
...data ...data
@ -51,8 +85,14 @@ export async function pageVisitByKey(data: IPageVisitByKey) {
}); });
} }
// 分页搜索老人 /**
* pageSearchElderByKey
* @param data - IPageSearchElderByKey
* @returns Promise
*/
export async function pageSearchElderByKey(data: IPageSearchElderByKey) { export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/visit/pageSearchElderByKey"
// 将 data 对象展开作为请求的参数
return http.get("/api/visit/pageSearchElderByKey", { return http.get("/api/visit/pageSearchElderByKey", {
params: { params: {
...data ...data
@ -60,13 +100,25 @@ export async function pageSearchElderByKey(data: IPageSearchElderByKey) {
}); });
} }
// 新增来访登记 /**
* addVisit访
* @param data - IAddVisit 访
* @returns Promise 访
*/
export function addVisit(data: IAddVisit) { export function addVisit(data: IAddVisit) {
// 使用导入的 http 对象发起 POST 请求,请求地址为 "/api/visit/addVisit"
// 将 data 对象作为请求的主体
return http.post("/api/visit/addVisit", data); return http.post("/api/visit/addVisit", data);
} }
// 根据编号获取来访登记 /**
* getVisitById访
* @param data - IGetVisitById 访
* @returns Promise 访
*/
export async function getVisitById(data: IGetVisitById) { export async function getVisitById(data: IGetVisitById) {
// 使用导入的 http 对象发起 GET 请求,请求地址为 "/api/visit/getVisitById"
// 将 data 对象展开作为请求的参数
return http.get("/api/visit/getVisitById", { return http.get("/api/visit/getVisitById", {
params: { params: {
...data ...data
@ -74,18 +126,36 @@ export async function getVisitById(data: IGetVisitById) {
}); });
} }
// 编辑来访登记 /**
* editVisit访
* @param data - IEditVisit 访
* @returns Promise 访
*/
export function editVisit(data: IEditVisit) { export function editVisit(data: IEditVisit) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/visit/editVisit"
// 将 data 对象作为请求的主体
return http.put("/api/visit/editVisit", data); return http.put("/api/visit/editVisit", data);
} }
// 登记离开 /**
* recordLeave
* @param data - IRecordLeave
* @returns Promise
*/
export function recordLeave(data: IRecordLeave) { export function recordLeave(data: IRecordLeave) {
// 使用导入的 http 对象发起 PUT 请求,请求地址为 "/api/visit/recordLeave"
// 将 data 对象作为请求的主体
return http.put("/api/visit/recordLeave", data); return http.put("/api/visit/recordLeave", data);
} }
// 删除来访登记 /**
* deleteVisit访
* @param data - IGetVisitById 访
* @returns Promise 访
*/
export async function deleteVisit(data: IGetVisitById) { export async function deleteVisit(data: IGetVisitById) {
// 使用导入的 http 对象发起 DELETE 请求,请求地址为 "/api/visit/deleteVisit"
// 将 data 对象展开作为请求的参数
return http.delete("/api/visit/deleteVisit", { return http.delete("/api/visit/deleteVisit", {
params: { params: {
...data ...data

@ -1,23 +1,38 @@
<template> <template>
<!-- 根元素 div通过 style 绑定动态样式v-show 根据 isShow 的值控制元素的显示与隐藏 -->
<div :style="style" v-show="isShow"> <div :style="style" v-show="isShow">
<!-- 插槽用于在父组件中插入内容 -->
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
<script setup lang="ts" name="GridItem"> <script setup lang="ts" name="GridItem">
// vue
import { computed, inject, Ref, ref, useAttrs, watch } from 'vue' import { computed, inject, Ref, ref, useAttrs, watch } from 'vue'
// '../interface/index' BreakPoint Responsive
import { BreakPoint, Responsive } from '../interface/index' import { BreakPoint, Responsive } from '../interface/index'
// Props
type Props = { type Props = {
// 0
offset?: number offset?: number
// 1
span?: number span?: number
// false
suffix?: boolean suffix?: boolean
// xs undefined
xs?: Responsive xs?: Responsive
// sm undefined
sm?: Responsive sm?: Responsive
// md undefined
md?: Responsive md?: Responsive
// lg undefined
lg?: Responsive lg?: Responsive
// xl undefined
xl?: Responsive xl?: Responsive
} }
// 使 withDefaults defineProps
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
offset: 0, offset: 0,
span: 1, span: 1,
@ -29,43 +44,63 @@ const props = withDefaults(defineProps<Props>(), {
xl: undefined xl: undefined
}) })
// props
const attrs = useAttrs() as any const attrs = useAttrs() as any
// isShow true
const isShow = ref(true) const isShow = ref(true)
// // breakPoint Ref<BreakPoint> ref('xl')
const breakPoint = inject<Ref<BreakPoint>>('breakPoint', ref('xl')) const breakPoint = inject<Ref<BreakPoint>>('breakPoint', ref('xl'))
// shouldHiddenIndex Ref<number> ref(-1)
const shouldHiddenIndex = inject<Ref<number>>('shouldHiddenIndex', ref(-1)) const shouldHiddenIndex = inject<Ref<number>>('shouldHiddenIndex', ref(-1))
// shouldHiddenIndex breakPoint
watch( watch(
// shouldHiddenIndex.value breakPoint.value
() => [shouldHiddenIndex.value, breakPoint.value], () => [shouldHiddenIndex.value, breakPoint.value],
// n shouldHiddenIndex.value breakPoint.value
n => { n => {
// attrs index
if (attrs.index) { if (attrs.index) {
isShow.value = !(n[0] !== -1 && parseInt(attrs.index) >= n[0]) //
isShow.value =!(n[0]!== -1 && parseInt(attrs.index) >= n[0])
} }
}, },
// immediate: true
{ immediate: true } { immediate: true }
) )
// gap 0
const gap = inject('gap', 0) const gap = inject('gap', 0)
// cols Ref<number> ref(4)
const cols = inject<Ref<number>>('cols', ref(4)) const cols = inject<Ref<number>>('cols', ref(4))
// style props
const style = computed(() => { const style = computed(() => {
let span = props[breakPoint.value]?.span ?? props.span // span 使 props.span
let offset = props[breakPoint.value]?.offset ?? props.offset let span = props[breakPoint.value]?.span?? props.span
// offset 使 props.offset
let offset = props[breakPoint.value]?.offset?? props.offset
//
if (props.suffix) { if (props.suffix) {
return { return {
// gridColumnStart
gridColumnStart: cols.value - span - offset + 1, gridColumnStart: cols.value - span - offset + 1,
// gridColumnEnd
gridColumnEnd: `span ${span + offset}`, gridColumnEnd: `span ${span + offset}`,
// marginLeft
marginLeft: marginLeft:
offset !== 0 offset!== 0
? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})`
: 'unset' : 'unset'
} }
} else { } else {
return { return {
// gridColumn
gridColumn: `span ${ gridColumn: `span ${
span + offset > cols.value ? cols.value : span + offset span + offset > cols.value? cols.value : span + offset
}/span ${span + offset > cols.value ? cols.value : span + offset}`, }/span ${span + offset > cols.value? cols.value : span + offset}`,
// marginLeft
marginLeft: marginLeft:
offset !== 0 offset!== 0
? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})` ? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})`
: 'unset' : 'unset'
} }

@ -1,10 +1,13 @@
<template> <template>
<!-- 根元素 div通过 style 绑定动态样式用于包裹插槽内容 -->
<div :style="style"> <div :style="style">
<!-- 插槽用于父组件插入内容 -->
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
<script setup lang="ts" name="Grid"> <script setup lang="ts" name="Grid">
// 'vue' Vue
import { import {
ref, ref,
watch, watch,
@ -19,50 +22,68 @@ import {
VNodeArrayChildren, VNodeArrayChildren,
VNode VNode
} from 'vue' } from 'vue'
// BreakPoint
import type { BreakPoint } from './interface/index' import type { BreakPoint } from './interface/index'
// Props
type Props = { type Props = {
//
cols?: number | Record<BreakPoint, number> cols?: number | Record<BreakPoint, number>
// false
collapsed?: boolean collapsed?: boolean
// 1
collapsedRows?: number collapsedRows?: number
// [, ]
gap?: [number, number] | number gap?: [number, number] | number
} }
// 使 withDefaults
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
//
cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }), cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
//
collapsed: false, collapsed: false,
// 1
collapsedRows: 1, collapsedRows: 1,
// 0
gap: 0 gap: 0
}) })
// collapsed true findIndex
onBeforeMount(() => props.collapsed && findIndex()) onBeforeMount(() => props.collapsed && findIndex())
// resize window resize
onMounted(() => { onMounted(() => {
resize({ target: { innerWidth: window.innerWidth } } as any) resize({ target: { innerWidth: window.innerWidth } } as any)
window.addEventListener('resize', resize) window.addEventListener('resize', resize)
}) })
// resize window resize
onActivated(() => { onActivated(() => {
resize({ target: { innerWidth: window.innerWidth } } as any) resize({ target: { innerWidth: window.innerWidth } } as any)
window.addEventListener('resize', resize) window.addEventListener('resize', resize)
}) })
// window resize
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', resize) window.removeEventListener('resize', resize)
}) })
// window resize
onDeactivated(() => { onDeactivated(() => {
window.removeEventListener('resize', resize) window.removeEventListener('resize', resize)
}) })
// // resize breakPoint
const resize = (e: UIEvent) => { const resize = (e: UIEvent) => {
//
let width = (e.target as Window).innerWidth let width = (e.target as Window).innerWidth
// breakPoint
switch (!!width) { switch (!!width) {
case width < 768: case width < 768:
breakPoint.value = 'xs' breakPoint.value = 'xs'
break break
case width >= 768 && width < 992: case width >= 768 && width < 992:
breakPoint.value = 'sm' breakPoint.value ='sm'
break break
case width >= 992 && width < 1200: case width >= 992 && width < 1200:
breakPoint.value = 'md' breakPoint.value ='md'
break break
case width >= 1200 && width < 1920: case width >= 1200 && width < 1920:
breakPoint.value = 'lg' breakPoint.value = 'lg'
@ -73,58 +94,66 @@ const resize = (e: UIEvent) => {
} }
} }
// gap // gap gap 使 gap
provide('gap', Array.isArray(props.gap) ? props.gap[0] : props.gap) provide('gap', Array.isArray(props.gap)? props.gap[0] : props.gap)
// // breakPoint
let breakPoint = ref<BreakPoint>('xl') let breakPoint = ref<BreakPoint>('xl')
provide('breakPoint', breakPoint) provide('breakPoint', breakPoint)
// index // index
const hiddenIndex = ref(-1) const hiddenIndex = ref(-1)
provide('shouldHiddenIndex', hiddenIndex) provide('shouldHiddenIndex', hiddenIndex)
// cols // cols cols breakPoint 使 cols
const cols = computed(() => { const cols = computed(() => {
if (typeof props.cols === 'object') if (typeof props.cols === 'object')
return props.cols[breakPoint.value] ?? props.cols return props.cols[breakPoint.value]?? props.cols
return props.cols return props.cols
}) })
provide('cols', cols) provide('cols', cols)
//
const slots = useSlots().default!() const slots = useSlots().default!()
// index // findIndex index
const findIndex = () => { const findIndex = () => {
//
let fields: VNodeArrayChildren = [] let fields: VNodeArrayChildren = []
//
let suffix: any = null let suffix: any = null
//
slots.forEach((slot: any) => { slots.forEach((slot: any) => {
// 'GridItem' suffix
if ( if (
typeof slot.type === 'object' && typeof slot.type === 'object' &&
slot.type.name === 'GridItem' && slot.type.name === 'GridItem' &&
slot.props?.suffix !== undefined slot.props?.suffix!== undefined
) )
suffix = slot suffix = slot
if (typeof slot.type === 'symbol' && Array.isArray(slot.children)) // symbol
if (typeof slot.type ==='symbol' && Array.isArray(slot.children))
slot.children.forEach((child: any) => fields.push(child)) slot.children.forEach((child: any) => fields.push(child))
}) })
// suffix // suffix
let suffixCols = 0 let suffixCols = 0
if (suffix) { if (suffix) {
suffixCols = suffixCols =
(suffix.props![breakPoint.value]?.span ?? suffix.props?.span ?? 1) + (suffix.props![breakPoint.value]?.span?? suffix.props?.span?? 1) +
(suffix.props![breakPoint.value]?.offset ?? suffix.props?.offset ?? 0) (suffix.props![breakPoint.value]?.offset?? suffix.props?.offset?? 0)
} }
try { try {
//
let find = false let find = false
//
fields.reduce((prev = 0, current, index) => { fields.reduce((prev = 0, current, index) => {
prev += prev +=
((current as VNode)!.props![breakPoint.value]?.span ?? ((current as VNode)!.props![breakPoint.value]?.span??
(current as VNode)!.props?.span ?? (current as VNode)!.props?.span??
1) + 1) +
((current as VNode)!.props![breakPoint.value]?.offset ?? ((current as VNode)!.props![breakPoint.value]?.offset??
(current as VNode)!.props?.offset ?? (current as VNode)!.props?.offset??
0) 0)
if ((prev as number) > props.collapsedRows * cols.value - suffixCols) { if ((prev as number) > props.collapsedRows * cols.value - suffixCols) {
hiddenIndex.value = index hiddenIndex.value = index
@ -133,13 +162,14 @@ const findIndex = () => {
} }
return prev return prev
}, 0) }, 0)
// hiddenIndex -1
if (!find) hiddenIndex.value = -1 if (!find) hiddenIndex.value = -1
} catch (e) { } catch (e) {
// console.warn(e); // console.warn(e);
} }
} }
// findIndex // breakPoint collapsed true findIndex
watch( watch(
() => breakPoint.value, () => breakPoint.value,
() => { () => {
@ -147,7 +177,7 @@ watch(
} }
) )
// collapsed // collapsed true findIndex hiddenIndex -1
watch( watch(
() => props.collapsed, () => props.collapsed,
value => { value => {
@ -156,14 +186,14 @@ watch(
} }
) )
// //
const gap = computed(() => { const gap = computed(() => {
if (typeof props.gap === 'number') return `${props.gap}px` if (typeof props.gap === 'number') return `${props.gap}px`
if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px` if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`
return 'unset' return 'unset'
}) })
// style //
const style = computed(() => { const style = computed(() => {
return { return {
display: 'grid', display: 'grid',
@ -172,5 +202,6 @@ const style = computed(() => {
} }
}) })
// breakPoint便访
defineExpose({ breakPoint }) defineExpose({ breakPoint })
</script> </script>

@ -1,6 +1,12 @@
export type BreakPoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' // 定义一个类型别名 BreakPoint它的值只能是 'xs'、'sm'、'md'、'lg' 或 'xl' 中的一个,
// 通常用于表示响应式布局中的不同屏幕断点
export type BreakPoint = 'xs' |'sm' |'md' | 'lg' | 'xl';
// 定义一个类型别名 Responsive它是一个对象类型包含两个可选属性 span 和 offset。
// span 用于表示在响应式布局中元素所占的列数,类型为 number 类型的可选值。
// offset 用于表示在响应式布局中元素的偏移量,类型为 number 类型的可选值。
// 这个类型通常用于描述在不同屏幕断点下元素的布局属性
export type Responsive = { export type Responsive = {
span?: number span?: number;
offset?: number offset?: number;
} };

@ -1,4 +1,12 @@
<template> <template>
<!-- Component Vue 中的动态组件:is="icon" 表示根据 icon 的值来动态渲染对应的图标组件 -->
<!-- :theme="theme" 绑定图标主题如轮廓outline填充filled -->
<!-- :size="size" 绑定图标大小可以是数字或字符串类型 -->
<!-- :spin="spin" 绑定图标是否旋转的状态布尔类型 -->
<!-- :fill="fill" 绑定图标的填充颜色可以是字符串或字符串数组 -->
<!-- :strokeLinecap="strokeLinecap" 绑定图标描边端点的样式 -->
<!-- :strokeLinejoin="strokeLinejoin" 绑定图标描边连接处的样式 -->
<!-- :strokeWidth="strokeWidth" 绑定图标描边的宽度 -->
<Component <Component
:is="icon" :is="icon"
:theme="theme" :theme="theme"
@ -12,16 +20,26 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// '@icon-park/vue-next/lib/runtime' Icon
import type { Icon } from '@icon-park/vue-next/lib/runtime' import type { Icon } from '@icon-park/vue-next/lib/runtime'
// 使 defineProps
defineProps<{ defineProps<{
// icon Icon
icon: Icon icon: Icon
theme?: 'outline' | 'filled' | 'two-tone' | 'multi-color' // theme 'outline' | 'filled' | 'two-tone' |'multi-color'
theme?: 'outline' | 'filled' | 'two-tone' |'multi-color'
// size number | string
size?: number | string size?: number | string
// spin boolean
spin?: boolean spin?: boolean
// fill string | string[]
fill?: string | string[] fill?: string | string[]
strokeLinecap?: 'butt' | 'round' | 'square' // strokeLinecap 'butt' | 'round' |'square'
strokeLinecap?: 'butt' | 'round' |'square'
// strokeLinejoin 'miter' | 'round' | 'bevel'
strokeLinejoin?: 'miter' | 'round' | 'bevel' strokeLinejoin?: 'miter' | 'round' | 'bevel'
// strokeWidth number
strokeWidth?: number strokeWidth?: number
}>() }>()
</script> </script>

@ -1,7 +1,18 @@
<template> <template>
<!-- 列设置 --> <!-- 注释说明该部分是列设置 -->
<!-- el-drawer Element Plus 中的抽屉组件用于展示列设置的内容 -->
<!-- title="列设置" 设置抽屉的标题 -->
<!-- v-model="drawerVisible" 双向绑定抽屉的显示隐藏状态drawerVisible 是一个响应式变量 -->
<!-- size="450px" 设置抽屉的宽度为 450px -->
<el-drawer title="列设置" v-model="drawerVisible" size="450px"> <el-drawer title="列设置" v-model="drawerVisible" size="450px">
<!-- 定义一个包含表格的 div 容器 -->
<div class="table-main"> <div class="table-main">
<!-- el-table Element Plus 中的表格组件用于展示列设置的数据 -->
<!-- :data="colSetting" 绑定表格的数据colSetting 是一个数组包含列的设置信息 -->
<!-- :border="true" 设置表格有边框 -->
<!-- row-key="prop" 为表格的每一行设置唯一的标识这里使用 prop 作为标识 -->
<!-- default-expand-all 表示默认展开所有行对于树形表格 -->
<!-- :tree-props="{ children: '_children' }" 配置表格为树形表格指定子节点的字段为 _children -->
<el-table <el-table
:data="colSetting" :data="colSetting"
:border="true" :border="true"
@ -9,23 +20,34 @@
default-expand-all default-expand-all
:tree-props="{ children: '_children' }" :tree-props="{ children: '_children' }"
> >
<!-- el-table-column 是表格列组件用于定义表格的列 -->
<!-- prop="label" 绑定列的数据字段为 label -->
<!-- align="center" 设置列内容居中对齐 -->
<!-- label="列名" 设置列的标题为列名 -->
<el-table-column prop="label" align="center" label="列名" /> <el-table-column prop="label" align="center" label="列名" />
<!-- 定义显示列prop="isShow" 绑定数据字段为 isShow -->
<!-- v-slot="scope" 定义作用域插槽scope 包含当前行的数据 -->
<el-table-column <el-table-column
prop="isShow" prop="isShow"
align="center" align="center"
label="显示" label="显示"
v-slot="scope" v-slot="scope"
> >
<!-- el-switch Element Plus 中的开关组件v-model="scope.row.isShow" 双向绑定当前行的 isShow 字段 -->
<el-switch v-model="scope.row.isShow"></el-switch> <el-switch v-model="scope.row.isShow"></el-switch>
</el-table-column> </el-table-column>
<!-- 定义排序列prop="sortable" 绑定数据字段为 sortable -->
<!-- v-slot="scope" 定义作用域插槽scope 包含当前行的数据 -->
<el-table-column <el-table-column
prop="sortable" prop="sortable"
align="center" align="center"
label="排序" label="排序"
v-slot="scope" v-slot="scope"
> >
<!-- el-switch Element Plus 中的开关组件v-model="scope.row.sortable" 双向绑定当前行的 sortable 字段 -->
<el-switch v-model="scope.row.sortable"></el-switch> <el-switch v-model="scope.row.sortable"></el-switch>
</el-table-column> </el-table-column>
<!-- 定义表格为空时显示的内容#empty 是表格的 empty 插槽 -->
<template #empty> <template #empty>
<div class="table-empty"> <div class="table-empty">
<div>暂无可配置列</div> <div>暂无可配置列</div>
@ -37,23 +59,29 @@
</template> </template>
<script setup lang="ts" name="ColSetting"> <script setup lang="ts" name="ColSetting">
// Vue ref
import { ref } from 'vue' import { ref } from 'vue'
// ProTable ColumnProps
import { ColumnProps } from '@/components/ProTable/interface' import { ColumnProps } from '@/components/ProTable/interface'
// 使 defineProps colSetting ColumnProps
defineProps<{ colSetting: ColumnProps[] }>() defineProps<{ colSetting: ColumnProps[] }>()
// drawerVisible false
const drawerVisible = ref<boolean>(false) const drawerVisible = ref<boolean>(false)
// // openColSetting drawerVisible true
const openColSetting = () => { const openColSetting = () => {
drawerVisible.value = true drawerVisible.value = true
} }
// openColSetting 便
defineExpose({ defineExpose({
openColSetting openColSetting
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// cursor-move move
.cursor-move { .cursor-move {
cursor: move; cursor: move;
} }

@ -1,5 +1,14 @@
<template> <template>
<!-- 分页组件 --> <!-- 注释说明该部分是分页组件 -->
<!-- el-pagination Element Plus 中的分页组件 -->
<!-- :current-page="pageable.pageNum" 双向绑定当前页码pageable.pageNum 是一个响应式变量表示当前页码 -->
<!-- :page-size="pageable.pageSize" 双向绑定每页显示的数量pageable.pageSize 是一个响应式变量表示每页显示的数量 -->
<!-- :page-sizes="[10, 25, 50, 100]" 设置每页显示数量的可选值 -->
<!-- :background="true" 设置分页组件的背景颜色 -->
<!-- layout="total, sizes, prev, pager, next, jumper" 设置分页组件的布局包括总数量每页显示数量选择器上一页页码下一页跳转到指定页 -->
<!-- :total="pageable.total" 绑定总数量pageable.total 是一个响应式变量表示数据的总数量 -->
<!-- @size-change="handleSizeChange" 监听每页显示数量改变的事件当用户选择不同的每页显示数量时会调用 handleSizeChange 函数 -->
<!-- @current-change="handleCurrentChange" 监听当前页码改变的事件当用户点击上一页下一页或跳转到指定页时会调用 handleCurrentChange 函数 -->
<el-pagination <el-pagination
:current-page="pageable.pageNum" :current-page="pageable.pageNum"
:page-size="pageable.pageSize" :page-size="pageable.pageSize"
@ -13,17 +22,26 @@
</template> </template>
<script setup lang="ts" name="Pagination"> <script setup lang="ts" name="Pagination">
// Pageable
// pageNum number
// pageSize number
// total number
interface Pageable { interface Pageable {
pageNum: number pageNum: number
pageSize: number pageSize: number
total: number total: number
} }
// PaginationProps
// pageable Pageable
// handleSizeChange number size
// handleCurrentChange number currentPage
interface PaginationProps { interface PaginationProps {
pageable: Pageable pageable: Pageable
handleSizeChange: (size: number) => void handleSizeChange: (size: number) => void
handleCurrentChange: (currentPage: number) => void handleCurrentChange: (currentPage: number) => void
} }
// 使 defineProps PaginationProps
defineProps<PaginationProps>() defineProps<PaginationProps>()
</script> </script>

@ -1,31 +1,42 @@
<template> <template>
<!-- 使用动态组件根据 renderLoop 函数的返回值来渲染不同的表格列 -->
<component :is="renderLoop(column)"></component> <component :is="renderLoop(column)"></component>
</template> </template>
<script lang="tsx" setup name="TableColumn"> <script lang="tsx" setup name="TableColumn">
// vue
import { inject, ref, useSlots } from 'vue' import { inject, ref, useSlots } from 'vue'
// ProTable ColumnProps
import { ColumnProps } from '@/components/ProTable/interface' import { ColumnProps } from '@/components/ProTable/interface'
//
import { filterEnum, formatValue, handleRowAccordingToProp } from '@/utils/util' import { filterEnum, formatValue, handleRowAccordingToProp } from '@/utils/util'
// 使 defineProps column ColumnProps
defineProps<{ column: ColumnProps }>() defineProps<{ column: ColumnProps }>()
//
const slots = useSlots() const slots = useSlots()
// enumMap Ref<Map<any, any>> Map
const enumMap = inject('enumMap', ref(new Map())) const enumMap = inject('enumMap', ref(new Map()))
// // renderCellData
const renderCellData = (item: ColumnProps, scope: { [key: string]: any }) => { const renderCellData = (item: ColumnProps, scope: { [key: string]: any }) => {
// prop enumMap
return enumMap.value.get(item.prop) && item.isFilterEnum return enumMap.value.get(item.prop) && item.isFilterEnum
// filterEnum
? filterEnum( ? filterEnum(
handleRowAccordingToProp(scope.row, item.prop!), handleRowAccordingToProp(scope.row, item.prop!),
enumMap.value.get(item.prop)!, enumMap.value.get(item.prop)!,
item.fieldNames item.fieldNames
) )
//
: formatValue(handleRowAccordingToProp(scope.row, item.prop!)) : formatValue(handleRowAccordingToProp(scope.row, item.prop!))
} }
// tag // getTagType
const getTagType = (item: ColumnProps, scope: { [key: string]: any }) => { const getTagType = (item: ColumnProps, scope: { [key: string]: any }) => {
// filterEnum
return filterEnum( return filterEnum(
handleRowAccordingToProp(scope.row, item.prop!), handleRowAccordingToProp(scope.row, item.prop!),
enumMap.value.get(item.prop), enumMap.value.get(item.prop),
@ -34,35 +45,53 @@ const getTagType = (item: ColumnProps, scope: { [key: string]: any }) => {
) as any ) as any
} }
// renderLoop
const renderLoop = (item: ColumnProps) => { const renderLoop = (item: ColumnProps) => {
return ( return (
<> <>
//
{item.isShow && ( {item.isShow && (
// el-table-column
<el-table-column <el-table-column
//
{...item} {...item}
align={item.align ?? 'center'} //
align={item.align?? 'center'}
// prop 'operation'
showOverflowTooltip={ showOverflowTooltip={
item.showOverflowTooltip ?? item.prop !== 'operation' item.showOverflowTooltip?? item.prop!== 'operation'
} }
> >
{{ {{
//
default: (scope: any) => { default: (scope: any) => {
//
if (item._children) if (item._children)
//
return item._children.map(child => renderLoop(child)) return item._children.map(child => renderLoop(child))
//
if (item.render) return item.render(scope) if (item.render) return item.render(scope)
// prop
if (slots[item.prop!]) return slots[item.prop!]!(scope) if (slots[item.prop!]) return slots[item.prop!]!(scope)
//
if (item.tag) if (item.tag)
return ( return (
// el-tag
<el-tag type={getTagType(item, scope)}> <el-tag type={getTagType(item, scope)}>
{renderCellData(item, scope)} {renderCellData(item, scope)}
</el-tag> </el-tag>
) )
//
return renderCellData(item, scope) return renderCellData(item, scope)
}, },
//
header: () => { header: () => {
//
if (item.headerRender) return item.headerRender(item) if (item.headerRender) return item.headerRender(item)
// propHeader
if (slots[`${item.prop}Header`]) if (slots[`${item.prop}Header`])
return slots[`${item.prop}Header`]!({ row: item }) return slots[`${item.prop}Header`]!({ row: item })
// label
return item.label return item.label
} }
}} }}

@ -2,6 +2,14 @@
<template> <template>
<!-- 查询表单 card --> <!-- 查询表单 card -->
<!-- SearchForm 是自定义的搜索表单组件 -->
<!-- :search="search" 绑定搜索方法 -->
<!-- :reset="reset" 绑定重置方法 -->
<!-- :searchParam="searchParam" 绑定搜索参数 -->
<!-- :columns="searchColumns" 绑定搜索列配置 -->
<!-- :searchCol="searchCol" 绑定搜索项每列占比配置 -->
<!-- v-show="isShowSearch" 根据 isShowSearch 的值显示或隐藏搜索表单 -->
<!-- ref="searchForm" 为搜索表单组件添加引用 -->
<SearchForm <SearchForm
:search="search" :search="search"
:reset="reset" :reset="reset"
@ -13,11 +21,12 @@
/> />
<!-- 表格内容 card --> <!-- 表格内容 card -->
<MyCard class="mt-2" <MyCard class="mt-2">
><div class="card table-main"> <div class="card table-main">
<div class="table-header"> <div class="table-header">
<div class="flex justify-between header-button-lf mb-2"> <div class="flex justify-between header-button-lf mb-2">
<div> <div>
<!-- 具名插槽 tableHeader传递了 selectedListIdsselectedList isSelected 数据 -->
<slot <slot
name="tableHeader" name="tableHeader"
:selectedListIds="selectedListIds" :selectedListIds="selectedListIds"
@ -27,15 +36,19 @@
</div> </div>
<div class="header-button-ri"> <div class="header-button-ri">
<!-- 具名插槽 toolButton包含了一些功能按钮 -->
<slot name="toolButton"> <slot name="toolButton">
<!-- 刷新按钮点击时调用 getTableList 方法获取表格数据 -->
<el-button :icon="Refresh" circle @click="getTableList" /> <el-button :icon="Refresh" circle @click="getTableList" />
<!-- <el-button :icon="Printer" circle v-if="columns.length" @click="handlePrint" /> --> <!-- <el-button :icon="Printer" circle v-if="columns.length" @click="handlePrint" /> -->
<!-- 列设置按钮 columns 存在时显示点击时调用 openColSetting 方法打开列设置 -->
<el-button <el-button
:icon="Operation" :icon="Operation"
circle circle
v-if="columns.length" v-if="columns.length"
@click="openColSetting" @click="openColSetting"
/> />
<!-- 搜索按钮 searchColumns 存在时显示点击时切换 isShowSearch 的值显示或隐藏搜索表单 -->
<el-button <el-button
:icon="Search" :icon="Search"
circle circle
@ -47,6 +60,13 @@
</div> </div>
</div> </div>
<!-- 表格主体 --> <!-- 表格主体 -->
<!-- el-table Element Plus 的表格组件 -->
<!-- ref="tableRef" 为表格组件添加引用 -->
<!-- v-bind="$attrs" 绑定父组件传递的所有属性 -->
<!-- :data="tableData" 绑定表格数据 -->
<!-- :border="border" 绑定表格是否有纵向边框 -->
<!-- :row-key="rowKey" 绑定行数据的唯一标识 -->
<!-- @selection-change="selectionChange" 监听表格行选择变化事件 -->
<el-table <el-table
ref="tableRef" ref="tableRef"
v-bind="$attrs" v-bind="$attrs"
@ -57,31 +77,35 @@
> >
<!-- 默认插槽 --> <!-- 默认插槽 -->
<slot></slot> <slot></slot>
<!-- 循环渲染表格列 -->
<template v-for="item in tableColumns" :key="item"> <template v-for="item in tableColumns" :key="item">
<!-- selection || index --> <!-- selection || index 类型的列 -->
<el-table-column <el-table-column
v-bind="item" v-bind="item"
:align="item.align ?? 'center'" :align="item.align?? 'center'"
:reserve-selection="item.type == 'selection'" :reserve-selection="item.type =='selection'"
v-if="item.type == 'selection' || item.type == 'index'" v-if="item.type =='selection' || item.type == 'index'"
> >
</el-table-column> </el-table-column>
<!-- expand 支持 tsx 语法 && 作用域插槽 (tsx > slot) --> <!-- expand 类型的列支持 tsx 语法和作用域插槽 -->
<el-table-column <el-table-column
v-bind="item" v-bind="item"
:align="item.align ?? 'center'" :align="item.align?? 'center'"
v-if="item.type == 'expand'" v-if="item.type == 'expand'"
v-slot="scope" v-slot="scope"
> >
<!-- 如果有自定义渲染函数则使用该函数渲染 -->
<component :is="item.render" :row="scope.row" v-if="item.render"> <component :is="item.render" :row="scope.row" v-if="item.render">
</component> </component>
<!-- 否则使用插槽渲染 -->
<slot :name="item.type" :row="scope.row" v-else></slot> <slot :name="item.type" :row="scope.row" v-else></slot>
</el-table-column> </el-table-column>
<!-- other 循环递归 --> <!-- 其他类型的列通过 TableColumn 组件递归渲染 -->
<TableColumn <TableColumn
v-if="!item.type && item.prop && item.isShow" v-if="!item.type && item.prop && item.isShow"
:column="item" :column="item"
> >
<!-- 循环渲染插槽内容 -->
<template v-for="slot in Object.keys($slots)" #[slot]="scope"> <template v-for="slot in Object.keys($slots)" #[slot]="scope">
<slot :name="slot" :row="scope.row"></slot> <slot :name="slot" :row="scope.row"></slot>
</template> </template>
@ -100,9 +124,10 @@
</div> </div>
</template> </template>
</el-table> </el-table>
<!-- 分页组件 --> <!-- 分页组件插槽 -->
<slot name="pagination"> <slot name="pagination">
<div class="mt-2 flex flex-row-reverse"> <div class="mt-2 flex flex-row-reverse">
<!-- Pagination 是自定义的分页组件 pagination true 时显示 -->
<Pagination <Pagination
v-if="pagination" v-if="pagination"
:pageable="pageable" :pageable="pageable"
@ -110,50 +135,78 @@
:handleCurrentChange="handleCurrentChange" :handleCurrentChange="handleCurrentChange"
/> />
</div> </div>
</slot></div </slot>
></MyCard> </div>
</MyCard>
<!-- 列设置 --> <!-- 列设置 -->
<!-- ColSetting 是自定义的列设置组件 toolButton true 时显示 -->
<!-- v-model:colSetting="colSetting" 双向绑定列设置数据 -->
<ColSetting v-if="toolButton" ref="colRef" v-model:colSetting="colSetting" /> <ColSetting v-if="toolButton" ref="colRef" v-model:colSetting="colSetting" />
</template> </template>
<script setup lang="ts" name="ProTable"> <script setup lang="ts" name="ProTable">
// Vue
import { ref, watch, computed, provide, onMounted } from 'vue' import { ref, watch, computed, provide, onMounted } from 'vue'
// Hooks
import { useTable } from '@/hooks/useTable' import { useTable } from '@/hooks/useTable'
//
import { BreakPoint } from '@/components/Grid/interface' import { BreakPoint } from '@/components/Grid/interface'
//
import { ColumnProps } from '@/components/ProTable/interface' import { ColumnProps } from '@/components/ProTable/interface'
// Element Plus
import { ElTable, TableProps } from 'element-plus' import { ElTable, TableProps } from 'element-plus'
// Element Plus
import { Refresh, Printer, Operation, Search } from '@element-plus/icons-vue' import { Refresh, Printer, Operation, Search } from '@element-plus/icons-vue'
//
import { import {
filterEnum, filterEnum,
formatValue, formatValue,
handleProp, handleProp,
handleRowAccordingToProp handleRowAccordingToProp
} from '@/utils/util' } from '@/utils/util'
//
import SearchForm from '@/components/SearchForm/index.vue' import SearchForm from '@/components/SearchForm/index.vue'
//
import Pagination from './components/Pagination.vue' import Pagination from './components/Pagination.vue'
//
import ColSetting from './components/ColSetting.vue' import ColSetting from './components/ColSetting.vue'
//
import TableColumn from './components/TableColumn.vue' import TableColumn from './components/TableColumn.vue'
// Hooks
import { useSelection } from '@/hooks/useSelection' import { useSelection } from '@/hooks/useSelection'
// import printJS from "print-js"; // import printJS from "print-js";
//
const searchForm = ref() const searchForm = ref()
// ProTable TableProps data
interface ProTableProps extends Partial<Omit<TableProps<any>, 'data'>> { interface ProTableProps extends Partial<Omit<TableProps<any>, 'data'>> {
columns: ColumnProps[] // //
requestApi: (params: any) => Promise<any> // api ==> columns: ColumnProps[]
// API
requestApi: (params: any) => Promise<any>
//
requestAuto?: boolean requestAuto?: boolean
dataCallback?: (data: any) => any // ==> //
title?: string // ==> dataCallback?: (data: any) => any
pagination?: boolean // ==> true //
initParam?: any // ==> {} title?: string
border?: boolean // ==> true // true
toolButton?: boolean // ==> true pagination?: boolean
rowKey?: string // Key Table id ==> id // {}
searchCol?: number | Record<BreakPoint, number> // ==> { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 } initParam?: any
// true
border?: boolean
// true
toolButton?: boolean
// Key Table id id
rowKey?: string
// { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
searchCol?: number | Record<BreakPoint, number>
} }
// //
const props = withDefaults(defineProps<ProTableProps>(), { const props = withDefaults(defineProps<ProTableProps>(), {
requestAuto: true, requestAuto: true,
columns: () => [], columns: () => [],
@ -165,17 +218,17 @@ const props = withDefaults(defineProps<ProTableProps>(), {
searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }) searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 })
}) })
// //
const isShowSearch = ref(true) const isShowSearch = ref(true)
// DOM // DOM
const tableRef = ref<InstanceType<typeof ElTable>>() const tableRef = ref<InstanceType<typeof ElTable>>()
// Hooks // 使 Hooks
const { selectionChange, selectedList, selectedListIds, isSelected } = const { selectionChange, selectedList, selectedListIds, isSelected } =
useSelection(props.rowKey) useSelection(props.rowKey)
// Hooks // 使 Hooks
const { const {
tableData, tableData,
pageable, pageable,
@ -193,90 +246,99 @@ const {
props.dataCallback props.dataCallback
) )
// //
const clearSelection = () => tableRef.value!.clearSelection() const clearSelection = () => tableRef.value!.clearSelection()
// // requestAuto true getTableList
onMounted(() => props.requestAuto && getTableList()) onMounted(() => props.requestAuto && getTableList())
// initParam // initParam
watch(() => props.initParam, getTableList, { deep: true }) watch(() => props.initParam, getTableList, { deep: true })
// columns //
const tableColumns = ref<ColumnProps[]>(props.columns) const tableColumns = ref<ColumnProps[]>(props.columns)
// enumMap enum || // enumMap provide
const enumMap = ref(new Map<string, { [key: string]: any }[]>()) const enumMap = ref(new Map<string, { [key: string]: any }[]>())
provide('enumMap', enumMap) provide('enumMap', enumMap)
// enumMap
const setEnumMap = async (col: ColumnProps) => { const setEnumMap = async (col: ColumnProps) => {
if (!col.enum) return if (!col.enum) return
// enum enumMap // enum enumMap
if (typeof col.enum !== 'function') if (typeof col.enum!== 'function')
return enumMap.value.set(col.prop!, col.enum!) return enumMap.value.set(col.prop!, col.enum!)
// enum enumMap
const { data } = await col.enum() const { data } = await col.enum()
enumMap.value.set(col.prop!, data) enumMap.value.set(col.prop!, data)
} }
// columns // columns
const flatColumnsFunc = ( const flatColumnsFunc = (
columns: ColumnProps[], columns: ColumnProps[],
flatArr: ColumnProps[] = [] flatArr: ColumnProps[] = []
) => { ) => {
columns.forEach(async col => { columns.forEach(async col => {
//
if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children)) if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children))
flatArr.push(col) flatArr.push(col)
// column isShow && isFilterEnum // column isShow isFilterEnum
col.isShow = col.isShow ?? true col.isShow = col.isShow?? true
col.isFilterEnum = col.isFilterEnum ?? true col.isFilterEnum = col.isFilterEnum?? true
// enumMap // enumMap
setEnumMap(col) setEnumMap(col)
}) })
return flatArr.filter(item => !item._children?.length) //
return flatArr.filter(item =>!item._children?.length)
} }
// flatColumns // columns
const flatColumns = ref<ColumnProps[]>() const flatColumns = ref<ColumnProps[]>()
flatColumns.value = flatColumnsFunc(tableColumns.value) flatColumns.value = flatColumnsFunc(tableColumns.value)
// //
const searchColumns = flatColumns.value.filter(item => item.search?.el) const searchColumns = flatColumns.value.filter(item => item.search?.el)
// && //
searchColumns.forEach((column, index) => { searchColumns.forEach((column, index) => {
column.search!.order = column.search!.order ?? index + 2 column.search!.order = column.search!.order?? index + 2
if ( if (
column.search?.defaultValue !== undefined && column.search?.defaultValue!== undefined &&
column.search?.defaultValue !== null column.search?.defaultValue!== null
) { ) {
searchInitParam.value[column.search.key ?? handleProp(column.prop!)] = searchInitParam.value[column.search.key?? handleProp(column.prop!)] =
column.search?.defaultValue column.search?.defaultValue
searchParam.value[column.search.key ?? handleProp(column.prop!)] = searchParam.value[column.search.key?? handleProp(column.prop!)] =
column.search?.defaultValue column.search?.defaultValue
} }
}) })
// //
searchColumns.sort((a, b) => a.search!.order! - b.search!.order!) searchColumns.sort((a, b) => a.search!.order! - b.search!.order!)
// ==> //
const colRef = ref() const colRef = ref()
//
const colSetting = tableColumns.value!.filter( const colSetting = tableColumns.value!.filter(
item => item =>
!['selection', 'index', 'expand'].includes(item.type!) && ![
item.prop !== 'operation' 'selection',
'index',
'expand'
].includes(item.type!) && item.prop!== 'operation'
) )
//
const openColSetting = () => colRef.value.openColSetting() const openColSetting = () => colRef.value.openColSetting()
// 201-238 // 201-238
// enum // enum
const printData = computed(() => { const printData = computed(() => {
const printDataList = JSON.parse( const printDataList = JSON.parse(
JSON.stringify( JSON.stringify(
selectedList.value.length ? selectedList.value : tableData.value selectedList.value.length? selectedList.value : tableData.value
) )
) )
// enum || prop && enum //
const needTransformCol = flatColumns.value!.filter( const needTransformCol = flatColumns.value!.filter(
item => item =>
(item.enum || (item.prop && item.prop.split('.').length > 1)) && (item.enum || (item.prop && item.prop.split('.').length > 1)) &&
@ -285,13 +347,13 @@ const printData = computed(() => {
needTransformCol.forEach(colItem => { needTransformCol.forEach(colItem => {
printDataList.forEach((tableItem: { [key: string]: any }) => { printDataList.forEach((tableItem: { [key: string]: any }) => {
tableItem[handleProp(colItem.prop!)] = tableItem[handleProp(colItem.prop!)] =
colItem.prop!.split('.').length > 1 && !colItem.enum colItem.prop!.split('.').length > 1 &&!colItem.enum
? formatValue(handleRowAccordingToProp(tableItem, colItem.prop!)) ? formatValue(handleRowAccordingToProp(tableItem, colItem.prop!))
: filterEnum( : filterEnum(
handleRowAccordingToProp(tableItem, colItem.prop!), handleRowAccordingToProp(tableItem, colItem.prop!),
enumMap.value.get(colItem.prop!), enumMap.value.get(colItem.prop!),
colItem.fieldNames colItem.fieldNames
) )
for (const key in tableItem) { for (const key in tableItem) {
if (tableItem[key] === null) if (tableItem[key] === null)
tableItem[key] = formatValue(tableItem[key]) tableItem[key] = formatValue(tableItem[key])
@ -318,7 +380,7 @@ const printData = computed(() => {
// }); // });
// }; // };
// () //
defineExpose({ defineExpose({
element: tableRef, element: tableRef,
tableData, tableData,

@ -1,17 +1,32 @@
// 从 "element-plus/es/components/table/src/table-column/defaults" 导入 TableColumnCtx 类型
// TableColumnCtx 可能是 Element Plus 中表格列的上下文类型,包含了表格列的一些默认属性和方法
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults"; import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
// 从 "@/components/Grid/interface" 导入 BreakPoint 和 Responsive 类型
// BreakPoint 可能表示响应式布局中的不同断点(如 xs, sm, md, lg, xl 等)
// Responsive 可能用于描述在不同断点下的响应式配置
import { BreakPoint, Responsive } from "@/components/Grid/interface"; import { BreakPoint, Responsive } from "@/components/Grid/interface";
// 定义一个接口 EnumProps用于描述枚举属性
export interface EnumProps { export interface EnumProps {
label: string; // 选项框显示的文字 // 选项框显示的文字,类型为字符串
value: any; // 选项框值 label: string;
disabled?: boolean; // 是否禁用此选项 // 选项框值,可以是任意类型
tagType?: string; // 当 tag 为 true 时,此选择会指定 tag 显示类型 value: any;
children?: EnumProps[]; // 为树形选择时,可以通过 children 属性指定子选项 // 是否禁用此选项,可选,默认为 undefined类型为布尔值
disabled?: boolean;
// 当 tag 为 true 时,此选择会指定 tag 显示类型,可选,默认为 undefined类型为字符串
tagType?: string;
// 为树形选择时,可以通过 children 属性指定子选项,可选,默认为 undefined类型为 EnumProps 数组
children?: EnumProps[];
// 允许添加任意其他属性,属性名和值的类型都为任意类型
[key: string]: any; [key: string]: any;
} }
export type TypeProp = "index" | "selection" | "expand" // 定义一个类型别名 TypeProp其值只能是 "index"、"selection" 或 "expand" 中的一个
export type TypeProp = "index" | "selection" | "expand";
// 定义一个类型别名 SearchType用于表示搜索框的类型
// 可以是 "input"、"input-number" 等多种 Element Plus 组件类型
export type SearchType = export type SearchType =
| "input" | "input"
| "input-number" | "input-number"
@ -23,27 +38,47 @@ export type SearchType =
| "time-picker" | "time-picker"
| "time-select" | "time-select"
| "switch" | "switch"
| "slider" | "slider";
// 定义一个接口 SearchProps用于描述搜索项的属性
export type SearchProps = { export type SearchProps = {
el: SearchType // 当前项搜索框的类型 // 当前项搜索框的类型,类型为 SearchType
props?: any // 搜索项参数,根据 element plus 官方文档来传递,该属性所有值会透传到组件 el: SearchType;
key?: string // 当搜索项 key 不为 prop 属性时,可通过 key 指定 // 搜索项参数,根据 element plus 官方文档来传递,该属性所有值会透传到组件,可选,默认为 undefined
order?: number // 搜索项排序(从大到小) props?: any;
span?: number // 搜索项所占用的列数默认为1列 // 当搜索项 key 不为 prop 属性时,可通过 key 指定,可选,默认为 undefined类型为字符串
offset?: number // 搜索字段左侧偏移列数 key?: string;
defaultValue?: string | number | boolean | any[] // 搜索项默认值 // 搜索项排序(从大到小),可选,默认为 undefined类型为数字
} & Partial<Record<BreakPoint, Responsive>> order?: number;
// 搜索项所占用的列数,默认为 1 列,可选,默认为 undefined类型为数字
span?: number;
// 搜索字段左侧偏移列数,可选,默认为 undefined类型为数字
offset?: number;
// 搜索项默认值,可以是字符串、数字、布尔值或任意类型的数组,可选,默认为 undefined
defaultValue?: string | number | boolean | any[];
// 扩展为包含 BreakPoint 类型的键和 Responsive 类型的值的部分记录,用于实现响应式配置
} & Partial<Record<BreakPoint, Responsive>>;
// 定义一个接口 ColumnProps用于描述表格列的属性
// 扩展自 Partial<Omit<TableColumnCtx<T>, "children" | "renderHeader" | "renderCell">>,表示部分继承 TableColumnCtx 类型并去除 "children"、"renderHeader" 和 "renderCell" 属性
export interface ColumnProps<T = any> export interface ColumnProps<T = any>
extends Partial<Omit<TableColumnCtx<T>, "children" | "renderHeader" | "renderCell">> { extends Partial<Omit<TableColumnCtx<T>, "children" | "renderHeader" | "renderCell">> {
tag?: boolean; // 是否是标签展示 // 是否是标签展示,可选,默认为 undefined类型为布尔值
isShow?: boolean; // 是否显示在表格当中 tag?: boolean;
search?: SearchProps | undefined; // 搜索项配置 // 是否显示在表格当中,可选,默认为 undefined类型为布尔值
enum?: EnumProps[] | ((params?: any) => Promise<any>); // 枚举类型(渲染值的字典) isShow?: boolean;
isFilterEnum?: boolean; // 当前单元格值是否根据 enum 格式化示例enum 只作为搜索项数据) // 搜索项配置,可选,默认为 undefined类型为 SearchProps 或 undefined
fieldNames?: { label: string; value: string }; // 指定 label && value 的 key 值 search?: SearchProps | undefined;
headerRender?: (row: ColumnProps) => any; // 自定义表头内容渲染tsx语法 // 枚举类型(渲染值的字典),可以是 EnumProps 数组或一个返回 Promise<any> 的函数,可选,默认为 undefined
render?: (scope: { row: T }) => any; // 自定义单元格内容渲染tsx语法 enum?: EnumProps[] | ((params?: any) => Promise<any>);
_children?: ColumnProps<T>[]; // 多级表头 // 当前单元格值是否根据 enum 格式化示例enum 只作为搜索项数据),可选,默认为 undefined类型为布尔值
isFilterEnum?: boolean;
// 指定 label && value 的 key 值,可选,默认为 undefined类型为包含 label 和 value 键的对象
fieldNames?: { label: string; value: string };
// 自定义表头内容渲染tsx语法可选默认为 undefined类型为一个接受 ColumnProps 类型参数并返回任意类型的函数
headerRender?: (row: ColumnProps) => any;
// 自定义单元格内容渲染tsx语法可选默认为 undefined类型为一个接受包含 row 属性且类型为 T 的对象并返回任意类型的函数
render?: (scope: { row: T }) => any;
// 多级表头,可选,默认为 undefined类型为 ColumnProps<T> 数组
_children?: ColumnProps<T>[];
} }

@ -1,6 +1,12 @@
// 从当前目录下的 src 文件夹中的 index.vue 文件导入组件
// 这里将导入的组件命名为 reImageVerify它应该是一个 Vue 组件
import reImageVerify from './src/index.vue' import reImageVerify from './src/index.vue'
/** 图形验证码组件 */ /** 图形验证码组件 */
// 使用具名导出的方式,将导入的组件以 ReImageVerify 这个名称导出
// 具名导出允许在导入时指定具体要导入的内容,方便在其他文件中使用
export const ReImageVerify = reImageVerify export const ReImageVerify = reImageVerify
// 使用默认导出的方式,将导入的组件作为默认导出
// 默认导出允许在导入时可以使用任意名称来接收该组件,通常一个文件只能有一个默认导出
export default ReImageVerify export default ReImageVerify

@ -5,23 +5,32 @@ import { ref, onMounted } from 'vue'
* @param width - * @param width -
* @param height - * @param height -
*/ */
// 定义一个名为 useImageVerify 的函数,用于生成和管理图形验证码,接受图形宽度和高度作为参数,有默认值
export const useImageVerify = (width = 120, height = 40) => { export const useImageVerify = (width = 120, height = 40) => {
// 创建一个响应式引用 domRef用于存储 <canvas> 元素的引用
const domRef = ref<HTMLCanvasElement>() const domRef = ref<HTMLCanvasElement>()
// 创建一个响应式变量 imgCode用于存储生成的图形验证码字符串
const imgCode = ref('') const imgCode = ref('')
// 定义一个函数 setImgCode用于设置 imgCode 的值
function setImgCode(code: string) { function setImgCode(code: string) {
imgCode.value = code imgCode.value = code
} }
// 定义一个函数 getImgCode用于在 <canvas> 上绘制验证码并获取生成的验证码字符串
function getImgCode() { function getImgCode() {
// 如果 domRef 没有值(即 <canvas> 元素未挂载),则直接返回
if (!domRef.value) return if (!domRef.value) return
// 调用 draw 函数绘制验证码,并将返回的验证码字符串赋值给 imgCode
imgCode.value = draw(domRef.value, width, height) imgCode.value = draw(domRef.value, width, height)
} }
// 在组件挂载后执行的钩子函数,调用 getImgCode 函数生成验证码
onMounted(() => { onMounted(() => {
getImgCode() getImgCode()
}) })
// 返回一个包含 domRef、imgCode、setImgCode 和 getImgCode 的对象,以便在其他地方使用这些变量和函数
return { return {
domRef, domRef,
imgCode, imgCode,
@ -30,56 +39,95 @@ export const useImageVerify = (width = 120, height = 40) => {
} }
} }
// 定义一个函数 randomNum用于生成指定范围内的随机整数
function randomNum(min: number, max: number) { function randomNum(min: number, max: number) {
// 使用 Math.random() 生成一个 0 到 1 之间的随机小数,然后通过计算得到指定范围内的整数
const num = Math.floor(Math.random() * (max - min) + min) const num = Math.floor(Math.random() * (max - min) + min)
return num return num
} }
// 定义一个函数 randomColor用于生成指定范围内的随机颜色RGB 格式)
function randomColor(min: number, max: number) { function randomColor(min: number, max: number) {
// 分别生成随机的红r、绿g、蓝b分量值
const r = randomNum(min, max) const r = randomNum(min, max)
const g = randomNum(min, max) const g = randomNum(min, max)
const b = randomNum(min, max) const b = randomNum(min, max)
// 返回 RGB 格式的颜色字符串
return `rgb(${r},${g},${b})` return `rgb(${r},${g},${b})`
} }
// 定义一个函数 draw用于在 <canvas> 元素上绘制图形验证码
function draw(dom: HTMLCanvasElement, width: number, height: number) { function draw(dom: HTMLCanvasElement, width: number, height: number) {
// 初始化一个空字符串 imgCode用于存储生成的验证码
let imgCode = '' let imgCode = ''
// 定义一个字符串 NUMBER_STRING包含数字 0 到 9用于生成验证码字符
const NUMBER_STRING = '0123456789' const NUMBER_STRING = '0123456789'
// 获取 <canvas> 元素的 2D 绘图上下文 ctx
const ctx = dom.getContext('2d') const ctx = dom.getContext('2d')
// 如果获取上下文失败ctx 为 null则直接返回空的验证码字符串
if (!ctx) return imgCode if (!ctx) return imgCode
// 设置绘图上下文的填充样式为随机颜色(背景色)
ctx.fillStyle = randomColor(180, 230) ctx.fillStyle = randomColor(180, 230)
// 使用填充样式绘制一个矩形,覆盖整个 <canvas> 区域,作为验证码的背景
ctx.fillRect(0, 0, width, height) ctx.fillRect(0, 0, width, height)
// 循环 4 次,生成 4 个验证码字符
for (let i = 0; i < 4; i += 1) { for (let i = 0; i < 4; i += 1) {
// 从 NUMBER_STRING 中随机选取一个字符作为验证码字符
const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)] const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)]
// 将选取的字符添加到 imgCode 字符串中
imgCode += text imgCode += text
// 随机生成字体大小
const fontSize = randomNum(18, 41) const fontSize = randomNum(18, 41)
// 随机生成字符的旋转角度
const deg = randomNum(-30, 30) const deg = randomNum(-30, 30)
// 设置绘图上下文的字体样式
ctx.font = `${fontSize}px Simhei` ctx.font = `${fontSize}px Simhei`
// 设置文本基线为顶部
ctx.textBaseline = 'top' ctx.textBaseline = 'top'
// 设置绘图上下文的填充样式为随机颜色(字符颜色)
ctx.fillStyle = randomColor(80, 150) ctx.fillStyle = randomColor(80, 150)
// 保存当前绘图上下文的状态
ctx.save() ctx.save()
// 平移绘图上下文的原点到指定位置(每个字符的起始位置)
ctx.translate(30 * i + 15, 15) ctx.translate(30 * i + 15, 15)
// 旋转绘图上下文指定的角度
ctx.rotate((deg * Math.PI) / 180) ctx.rotate((deg * Math.PI) / 180)
// 在指定位置绘制字符
ctx.fillText(text, -15 + 5, -15) ctx.fillText(text, -15 + 5, -15)
// 恢复之前保存的绘图上下文状态
ctx.restore() ctx.restore()
} }
// 循环 5 次,绘制 5 条随机直线
for (let i = 0; i < 5; i += 1) { for (let i = 0; i < 5; i += 1) {
// 开始绘制路径
ctx.beginPath() ctx.beginPath()
// 设置路径的起始点为随机位置
ctx.moveTo(randomNum(0, width), randomNum(0, height)) ctx.moveTo(randomNum(0, width), randomNum(0, height))
// 设置路径的终点为随机位置
ctx.lineTo(randomNum(0, width), randomNum(0, height)) ctx.lineTo(randomNum(0, width), randomNum(0, height))
// 设置绘图上下文的描边样式为随机颜色
ctx.strokeStyle = randomColor(180, 230) ctx.strokeStyle = randomColor(180, 230)
// 关闭路径
ctx.closePath() ctx.closePath()
// 绘制路径(直线)
ctx.stroke() ctx.stroke()
} }
// 循环 41 次,绘制 41 个随机圆形
for (let i = 0; i < 41; i += 1) { for (let i = 0; i < 41; i += 1) {
// 开始绘制路径
ctx.beginPath() ctx.beginPath()
// 绘制一个圆形,圆心为随机位置,半径为 1
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI) ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI)
// 关闭路径
ctx.closePath() ctx.closePath()
// 设置绘图上下文的填充样式为随机颜色
ctx.fillStyle = randomColor(150, 200) ctx.fillStyle = randomColor(150, 200)
// 填充圆形
ctx.fill() ctx.fill()
} }
// 返回生成的验证码字符串
return imgCode return imgCode
} }

@ -1,37 +1,50 @@
<script setup lang="ts"> <script setup lang="ts">
// Vue watch
import { watch } from 'vue' import { watch } from 'vue'
// useImageVerify
import { useImageVerify } from './hooks' import { useImageVerify } from './hooks'
// Props
interface Props { interface Props {
code?: string code?: string // code
} }
// Emits
interface Emits { interface Emits {
(e: 'update:code', code: string): void (e: 'update:code', code: string): void // 'update:code' code
} }
// 使 withDefaults props
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
code: '' code: '' // code
}) })
// emit
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
// useImageVerify domRefimgCodesetImgCode getImgCode
const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify() const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify()
// props.code props.code setImgCode
watch( watch(
() => props.code, () => props.code,
newValue => { newValue => {
setImgCode(newValue) setImgCode(newValue)
} }
) )
// imgCode imgCode 'update:code'
watch(imgCode, newValue => { watch(imgCode, newValue => {
emit('update:code', newValue) emit('update:code', newValue)
}) })
// getImgCode 便
defineExpose({ getImgCode }) defineExpose({ getImgCode })
</script> </script>
<template> <template>
<!-- 定义一个 canvas 元素设置其宽度为 120高度为 40添加类名 cursor-pointer 使其具有指针样式
绑定 ref domRef以便在脚本中访问该元素绑定 click 事件为 getImgCode 函数 -->
<canvas <canvas
ref="domRef" ref="domRef"
width="120" width="120"

@ -1,66 +1,76 @@
<template> <template>
<!-- 根据条件渲染不同的组件 -->
<component <component
v-if="column.search?.el" v-if="column.search?.el" <!-- 如果 column.search.el 存在则渲染该组件 -->
:is="`el-${column.search.el}`" :is="`el-${column.search.el}`" <!-- 根据 column.search.el 的值动态渲染对应的 Element Plus 组件例如 'el-input''el-select' -->
v-bind="handleSearchProps" v-bind="handleSearchProps" <!-- 绑定处理后的搜索属性 -->
v-model.trim="searchParam[column.search.key ?? handleProp(column.prop!)]" v-model.trim="searchParam[column.search.key?? handleProp(column.prop!)]" <!-- 双向绑定搜索参数使用 trim 修饰符去除首尾空格 -->
:data="column.search?.el === 'tree-select' ? columnEnum : []" :data="column.search?.el === 'tree-select'? columnEnum : []" <!-- 如果是 tree-select 组件绑定 columnEnum 数据否则绑定空数组 -->
:options=" :options="
['cascader', 'select-v2'].includes(column.search?.el) ? columnEnum : [] ['cascader','select-v2'].includes(column.search?.el)? columnEnum : []
" " <!-- 如果是 cascader select-v2 组件绑定 columnEnum 数据作为选项否则绑定空数组 -->
:placeholder="placeholder" :placeholder="placeholder" <!-- 绑定占位符文本 -->
:clearable="clearable" :clearable="clearable" <!-- 绑定是否可清除的属性 -->
range-separator="至" range-separator="至" <!-- 日期范围选择器的分隔符 -->
start-placeholder="开始时间" start-placeholder="开始时间" <!-- 日期范围选择器开始时间的占位符 -->
end-placeholder="结束时间" end-placeholder="结束时间" <!-- 日期范围选择器结束时间的占位符 -->
> >
<template #default="{ data }" v-if="column.search.el === 'cascader'"> <!-- column.search.el 'cascader' 渲染默认插槽内容 -->
<span>{{ data[fieldNames.label] }}</span> <template #default="{ data }" v-if="column.search.el === 'cascader'">
</template> <span>{{ data[fieldNames.label] }}</span> <!-- 显示数据的 label 字段 -->
<template v-if="column.search.el === 'select'"> </template>
<component <!-- column.search.el 'select' 渲染子选项 -->
:is="`el-option`" <template v-if="column.search.el ==='select'">
v-for="(col, index) in columnEnum" <component
:key="index" :is="`el-option`" <!-- 渲染 Element Plus el-option 组件 -->
:label="col[fieldNames.label]" v-for="(col, index) in columnEnum" <!-- 遍历 columnEnum 数据 -->
:value="col[fieldNames.value]" :key="index" <!-- 设置唯一的 key -->
></component> :label="col[fieldNames.label]" <!-- 绑定选项的 label -->
</template> :value="col[fieldNames.value]" <!-- 绑定选项的值 -->
<slot v-else></slot> ></component>
</template>
<!-- 如果以上条件都不满足渲染插槽内容 -->
<slot v-else></slot>
</component> </component>
</template> </template>
<script setup lang="ts" name="SearchFormItem"> <script setup lang="ts" name="SearchFormItem">
// Vue
import { computed, inject, onMounted, ref } from 'vue' import { computed, inject, onMounted, ref } from 'vue'
// handleProp
import { handleProp } from '@/utils/util' import { handleProp } from '@/utils/util'
//
import { ColumnProps } from '@/components/ProTable/interface' import { ColumnProps } from '@/components/ProTable/interface'
// SearchFormItem
interface SearchFormItem { interface SearchFormItem {
column: ColumnProps column: ColumnProps //
searchParam: { [key: string]: any } searchParam: { [key: string]: any } //
} }
// props
const props = defineProps<SearchFormItem>() const props = defineProps<SearchFormItem>()
// fieldNames label && value key // fieldNames label && value key
const fieldNames = computed(() => { const fieldNames = computed(() => {
return { return {
label: props.column.fieldNames?.label ?? 'label', label: props.column.fieldNames?.label?? 'label', // column.fieldNames.label 使使 'label'
value: props.column.fieldNames?.value ?? 'value' value: props.column.fieldNames?.value?? 'value' // column.fieldNames.value 使使 'value'
} }
}) })
// enumMap // enumMap
const enumMap = inject('enumMap', ref(new Map())) const enumMap = inject('enumMap', ref(new Map()))
// columnEnum props.column.prop enumMap
const columnEnum = computed(() => { const columnEnum = computed(() => {
let enumData = enumMap.value.get(props.column.prop) let enumData = enumMap.value.get(props.column.prop)
if (!enumData) return [] if (!enumData) return [] //
if (props.column.search?.el === 'select-v2' && props.column.fieldNames) { if (props.column.search?.el ==='select-v2' && props.column.fieldNames) {
enumData = enumData.map((item: { [key: string]: any }) => { enumData = enumData.map((item: { [key: string]: any }) => {
return { return {
...item, ...item,
label: item[fieldNames.value.label], label: item[fieldNames.value.label], // label
value: item[fieldNames.value.value] value: item[fieldNames.value.value] // value
} }
}) })
} }
@ -72,18 +82,18 @@ const handleSearchProps = computed(() => {
const label = fieldNames.value.label const label = fieldNames.value.label
const value = fieldNames.value.value const value = fieldNames.value.value
const searchEl = props.column.search?.el const searchEl = props.column.search?.el
const searchProps = props.column.search?.props ?? {} const searchProps = props.column.search?.props?? {}
let handleProps = searchProps let handleProps = searchProps
if (searchEl === 'tree-select') if (searchEl === 'tree-select')
handleProps = { handleProps = {
...searchProps, ...searchProps,
props: { label, ...searchProps.props }, props: { label,...searchProps.props }, // tree-select props
nodeKey: value nodeKey: value
} }
if (searchEl === 'cascader') if (searchEl === 'cascader')
handleProps = { handleProps = {
...searchProps, ...searchProps,
props: { label, value, ...searchProps.props } props: { label, value,...searchProps.props } // cascader props
} }
return handleProps return handleProps
}) })
@ -92,7 +102,7 @@ const handleSearchProps = computed(() => {
const placeholder = computed(() => { const placeholder = computed(() => {
const search = props.column.search const search = props.column.search
return ( return (
search?.props?.placeholder ?? (search?.el === 'input' ? '请输入' : '请选择') search?.props?.placeholder?? (search?.el === 'input'? '请输入' : '请选择') //
) )
}) })
@ -100,8 +110,8 @@ const placeholder = computed(() => {
const clearable = computed(() => { const clearable = computed(() => {
const search = props.column.search const search = props.column.search
return ( return (
search?.props?.clearable ?? search?.props?.clearable??
(search?.defaultValue == null || search?.defaultValue == undefined) (search?.defaultValue == null || search?.defaultValue == undefined) // props
) )
}) })
</script> </script>

@ -1,25 +1,34 @@
<template> <template>
<MyCard <!-- MyCard 组件可能是一个自定义的卡片组件 -->
><div class="card table-search" v-if="columns.length"> <MyCard>
<!-- columns 数组长度大于 0 时显示搜索表单 -->
<div class="card table-search" v-if="columns.length">
<!-- el-form 组件用于创建表单 -->
<el-form ref="formRef" :model="searchParam"> <el-form ref="formRef" :model="searchParam">
<!-- Grid 组件可能是一个用于布局的网格组件 -->
<Grid <Grid
ref="gridRef" ref="gridRef"
:collapsed="collapsed" :collapsed="collapsed"
:gap="[20, 0]" :gap="[20, 0]"
:cols="searchCol" :cols="searchCol"
> >
<!-- 循环渲染 GridItem 组件每个 GridItem 对应一个搜索表单字段 -->
<GridItem <GridItem
v-for="(item, index) in columns" v-for="(item, index) in columns"
:key="item.prop" :key="item.prop"
v-bind="getResponsive(item)" v-bind="getResponsive(item)"
:index="index" :index="index"
> >
<!-- el-form-item 组件用于创建表单字段 -->
<el-form-item :label="`${item.label} :`"> <el-form-item :label="`${item.label} :`">
<!-- SearchFormItem 组件用于渲染具体的搜索表单字段 -->
<SearchFormItem :column="item" :searchParam="searchParam" /> <SearchFormItem :column="item" :searchParam="searchParam" />
</el-form-item> </el-form-item>
</GridItem> </GridItem>
<!-- 带有 suffix 标志的 GridItem 组件用于放置操作按钮 -->
<GridItem suffix> <GridItem suffix>
<div class="operation"> <div class="operation">
<!-- 搜索按钮点击时调用 search 方法 -->
<el-button <el-button
class="bg-blue clickSearchBtn" class="bg-blue clickSearchBtn"
type="primary" type="primary"
@ -28,53 +37,74 @@
> >
搜索 搜索
</el-button> </el-button>
<!-- 重置按钮点击时调用 reset 方法 -->
<el-button :icon="Delete" @click="reset"></el-button> <el-button :icon="Delete" @click="reset"></el-button>
<!-- 展开/合并按钮根据 collapsed 的值显示不同的文本和图标 -->
<el-button <el-button
v-if="showCollapse" v-if="showCollapse"
link link
class="search-isOpen" class="search-isOpen"
@click="collapsed = !collapsed" @click="collapsed =!collapsed"
> >
{{ collapsed ? '展开' : '合并' }} {{ collapsed? '展开' : '合并' }}
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
<component :is="collapsed ? ArrowDown : ArrowUp"></component> <component :is="collapsed? ArrowDown : ArrowUp"></component>
</el-icon> </el-icon>
</el-button> </el-button>
</div> </div>
</GridItem> </GridItem>
</Grid> </Grid>
</el-form></div </el-form>
></MyCard> </div>
</MyCard>
</template> </template>
<script setup lang="ts" name="SearchForm"> <script setup lang="ts" name="SearchForm">
// Vue
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
// ProTable
import { ColumnProps } from '@/components/ProTable/interface' import { ColumnProps } from '@/components/ProTable/interface'
// Grid
import { BreakPoint } from '@/components/Grid/interface' import { BreakPoint } from '@/components/Grid/interface'
// Element Plus
import { Delete, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue' import { Delete, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue'
// SearchFormItem
import SearchFormItem from './components/SearchFormItem.vue' import SearchFormItem from './components/SearchFormItem.vue'
// Grid
import Grid from '@/components/Grid/index.vue' import Grid from '@/components/Grid/index.vue'
// GridItem
import GridItem from '@/components/Grid/components/GridItem.vue' import GridItem from '@/components/Grid/components/GridItem.vue'
// MyCard
import MyCard from '../my-card/my-card.vue' import MyCard from '../my-card/my-card.vue'
// ProTableProps
interface ProTableProps { interface ProTableProps {
columns?: ColumnProps[] // //
searchParam?: { [key: string]: any } // columns?: ColumnProps[]
//
searchParam?: { [key: string]: any }
//
searchCol: number | Record<BreakPoint, number> searchCol: number | Record<BreakPoint, number>
search: (params: any) => void // //
reset: (params: any) => void // search: (params: any) => void
//
reset: (params: any) => void
} }
// // 使 withDefaults
const props = withDefaults(defineProps<ProTableProps>(), { const props = withDefaults(defineProps<ProTableProps>(), {
columns: () => [], columns: () => [],
searchParam: () => ({}) searchParam: () => ({})
}) })
// //
const getResponsive = (item: ColumnProps) => { const getResponsive = (item: ColumnProps) => {
return { return {
// item.search.span
span: item.search?.span, span: item.search?.span,
offset: item.search?.offset ?? 0, // item.search.offset 0
offset: item.search?.offset?? 0,
// item.search
xs: item.search?.xs, xs: item.search?.xs,
sm: item.search?.sm, sm: item.search?.sm,
md: item.search?.md, md: item.search?.md,
@ -83,21 +113,24 @@ const getResponsive = (item: ColumnProps) => {
} }
} }
// //
const collapsed = ref(true) const collapsed = ref(true)
// // ref Grid
const gridRef = ref() const gridRef = ref()
//
const breakPoint = computed<BreakPoint>(() => gridRef.value?.breakPoint) const breakPoint = computed<BreakPoint>(() => gridRef.value?.breakPoint)
// / // /
const showCollapse = computed(() => { const showCollapse = computed(() => {
let show = false let show = false
// columns
props.columns.reduce((prev, current) => { props.columns.reduce((prev, current) => {
prev += prev +=
(current.search![breakPoint.value]?.span ?? current.search?.span ?? 1) + (current.search![breakPoint.value]?.span?? current.search?.span?? 1) +
(current.search![breakPoint.value]?.offset ?? current.search?.offset ?? 0) (current.search![breakPoint.value]?.offset?? current.search?.offset?? 0)
if (typeof props.searchCol !== 'number') { // searchCol /
if (typeof props.searchCol!== 'number') {
if (prev >= props.searchCol[breakPoint.value]) show = true if (prev >= props.searchCol[breakPoint.value]) show = true
} else { } else {
if (prev > props.searchCol) show = true if (prev > props.searchCol) show = true

@ -1,36 +1,49 @@
<template> <template>
<!-- SVG 图标容器添加了 svg-icon 类用于样式设置area-hidden="true" 用于隐藏无障碍区域 -->
<svg class="svg-icon" area-hidden="true"> <svg class="svg-icon" area-hidden="true">
<!-- use 元素用于引用外部 SVG 符号:xlink:href 绑定了动态的图标名称 -->
<use :xlink:href="iconName"></use> <use :xlink:href="iconName"></use>
</svg> </svg>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// Vue defineProps computed
import { defineProps, computed } from 'vue' import { defineProps, computed } from 'vue'
//
const props = defineProps({ const props = defineProps({
// icon
icon: { icon: {
type: String, type: String,
required: true required: true
}, },
// size 16
size: { size: {
type: [Number, String], type: [Number, String],
default: 16 default: 16
} }
}) })
// icon #icon-
const iconName = computed(() => { const iconName = computed(() => {
return `#icon-${props.icon}` return `#icon-${props.icon}`
}) })
// size
const iconSize = computed(() => { const iconSize = computed(() => {
return props.size + 'px' return props.size + 'px'
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .svg-icon SVG
.svg-icon { .svg-icon {
// 1em
width: 1em; width: 1em;
// 1em
height: 1em; height: 1em;
//
fill: currentColor; fill: currentColor;
// iconSize
font-size: v-bind(iconSize); font-size: v-bind(iconSize);
} }
</style> </style>

@ -1,26 +1,47 @@
<template> <template>
<!-- el-dialog Element Plus 中的对话框组件用于展示选择老人的界面 -->
<!-- style="width: 70%" 设置对话框的宽度为父容器的 70% -->
<!-- v-model="dialogVisible" 双向绑定对话框的显示隐藏状态dialogVisible 是一个响应式变量 -->
<!-- title="选择老人" 设置对话框的标题 -->
<!-- destroy-on-close 表示在对话框关闭时销毁其内容 -->
<el-dialog <el-dialog
style="width: 70%" style="width: 70%"
v-model="dialogVisible" v-model="dialogVisible"
title="选择老人" title="选择老人"
destroy-on-close destroy-on-close
> >
<!-- 定义一个包含表格的 div 容器 -->
<div class="table-box"> <div class="table-box">
<!-- ProTable 是自定义的表格组件用于展示用户列表 -->
<!-- ref="proTable" 为表格组件添加引用方便在脚本中访问 -->
<!-- title="用户列表" 设置表格的标题 -->
<!-- :columns="columns" 绑定表格的列配置项columns 是一个数组包含每列的信息 -->
<!-- :requestApi="getTableList" 绑定数据请求方法getTableList 用于获取表格数据 -->
<ProTable <ProTable
ref="proTable" ref="proTable"
title="用户列表" title="用户列表"
:columns="columns" :columns="columns"
:requestApi="getTableList" :requestApi="getTableList"
> >
<!-- 表格操作 --> <!-- 定义表格操作列的插槽内容 -->
<template #operation="scope"> <template #operation="scope">
<!-- el-popconfirm Element Plus 中的弹出确认框组件 -->
<!-- title="Are you sure to choose this?" 设置确认框的提示信息 -->
<!-- @confirm="checkElder(scope.row)" 当用户确认时调用 checkElder 方法并传递当前行的数据 -->
<!-- confirm-button-type="warning" 设置确认按钮的类型为警告样式 -->
<el-popconfirm <el-popconfirm
title="Are you sure to choose this?" title="Are you sure to choose this?"
@confirm="checkElder(scope.row)" @confirm="checkElder(scope.row)"
confirm-button-type="warning" confirm-button-type="warning"
> >
<!-- 定义确认框的触发元素 -->
<template #reference> <template #reference>
<el-button size="small" link :icon="View" > 选择 </el-button> <!-- el-button Element Plus 中的按钮组件 -->
<!-- size="small" 设置按钮大小为小 -->
<!-- link 设置按钮为链接样式 -->
<!-- :icon="View" 绑定图标View 是从 @element-plus/icons-vue 导入的图标组件 -->
<!-- 选择 是按钮的文本内容 -->
<el-button size="small" link :icon="View">选择</el-button>
</template> </template>
</el-popconfirm> </el-popconfirm>
</template> </template>
@ -30,27 +51,36 @@
</template> </template>
<script setup lang="ts" name="useProTable"> <script setup lang="ts" name="useProTable">
// Vue ref
import { ref } from "vue"; import { ref } from "vue";
// ProTable ColumnProps
import { ColumnProps } from "@/components/ProTable/interface"; import { ColumnProps } from "@/components/ProTable/interface";
// ProTable
import ProTable from "@/components/ProTable/index.vue"; import ProTable from "@/components/ProTable/index.vue";
// Element Plus View
import { View } from "@element-plus/icons-vue"; import { View } from "@element-plus/icons-vue";
// dialogVisible
const dialogVisible = ref(false); const dialogVisible = ref(false);
// proTable ProTable
const proTable = ref(); const proTable = ref();
// drawerProps DialogProps
const drawerProps = ref<DialogProps>(); const drawerProps = ref<DialogProps>();
// DialogProps elderApi
interface DialogProps { interface DialogProps {
elderApi: (params: any) => Promise<any>; elderApi: (params: any) => Promise<any>;
} }
// params // getTableList params
// ProTable :requestApi="getUserList" //
// elderApi
let getTableList = async (params: any) => { let getTableList = async (params: any) => {
let newParams = JSON.parse(JSON.stringify(params)); let newParams = JSON.parse(JSON.stringify(params));
return drawerProps.value?.elderApi(newParams); return drawerProps.value?.elderApi(newParams);
}; };
// // columns ColumnProps
const columns: ColumnProps<any>[] = [ const columns: ColumnProps<any>[] = [
{ prop: "rank", label: "序号", width: 55 }, { prop: "rank", label: "序号", width: 55 },
{ prop: "name", label: "姓名", search: { el: "input" } }, { prop: "name", label: "姓名", search: { el: "input" } },
@ -62,21 +92,23 @@ const columns: ColumnProps<any>[] = [
{ prop: "operation", label: "操作", width: 70 } { prop: "operation", label: "操作", width: 70 }
]; ];
// // elderAcceptParams DialogProps params
// drawerProps
const elderAcceptParams = (params: DialogProps) => { const elderAcceptParams = (params: DialogProps) => {
drawerProps.value = params; drawerProps.value = params;
dialogVisible.value = true; dialogVisible.value = true;
}; };
// // elderAcceptParams 便
defineExpose({ defineExpose({
elderAcceptParams elderAcceptParams
}); });
// const emit = defineEmits(['getCheckElderInfo']) // emit
const emit = defineEmits<{ const emit = defineEmits<{
(event: "getCheckElderInfo", val: any): void (event: "getCheckElderInfo", val: any): void
}>(); }>();
// // checkElder row
// "getCheckElderInfo"
const checkElder = (row: any) => { const checkElder = (row: any) => {
emit("getCheckElderInfo", row); emit("getCheckElderInfo", row);
dialogVisible.value = false; dialogVisible.value = false;

@ -1,17 +1,27 @@
<template> <template>
<!-- 根元素 div应用了多个类名用于设置样式和布局 -->
<!-- class="flex flex-col rounded card-wrap p-3" 表示使用 flex 布局方向为列边框圆角应用 card-wrap 类的样式内边距为 3 -->
<div class="flex flex-col rounded card-wrap p-3"> <div class="flex flex-col rounded card-wrap p-3">
<!-- 条件渲染的 div如果 title 存在即不是 undefined 或空字符串等假值则显示该 div -->
<!-- class="pb-2" 表示该元素底部外边距为 2 -->
<!-- v-if="title" Vue 的条件渲染指令 -->
<div class="pb-2" v-if="title">{{ title }}</div> <div class="pb-2" v-if="title">{{ title }}</div>
<!-- 插槽用于父组件插入内容会被父组件的内容替换 -->
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// 使 defineProps
// title ?
defineProps<{ defineProps<{
title?: string title?: string
}>() }>()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .card-wrap
// 1px 线 #eee 18px
.card-wrap { .card-wrap {
background-color: #ffffff; background-color: #ffffff;
border: 1px solid #eee; border: 1px solid #eee;

@ -1,10 +1,20 @@
<template> <template>
<!-- Element Plus 的对话框组件 el-dialog -->
<!-- style="width: 70%" 设置对话框的宽度为父容器的 70% -->
<!-- v-model="dialogVisible" 双向绑定对话框的显示隐藏状态dialogVisible 为一个响应式变量 -->
<!-- title="选择床位" 设置对话框的标题为选择床位 -->
<!-- destroy-on-close 表示在对话框关闭时销毁其中的内容 -->
<el-dialog <el-dialog
style="width: 70%" style="width: 70%"
v-model="dialogVisible" v-model="dialogVisible"
title="选择床位" title="选择床位"
destroy-on-close destroy-on-close
> >
<!-- Element Plus 的树状组件 el-tree -->
<!-- :data="data" 绑定树状结构的数据data 是一个响应式变量 -->
<!-- :props="defaultProps" 配置树状结构的属性如节点的标识显示文本子节点的字段等 -->
<!-- accordion 使树状结构以手风琴模式显示即同一时间只有一个节点可以展开 -->
<!-- @node-click="checkBed" 监听节点点击事件当点击节点时调用 checkBed 函数 -->
<el-tree <el-tree
:data="data" :data="data"
:props="defaultProps" :props="defaultProps"
@ -15,43 +25,63 @@
</template> </template>
<script setup lang="ts" name="useProTable"> <script setup lang="ts" name="useProTable">
// Vue ref
import { ref } from "vue"; import { ref } from "vue";
// API getBuildTree
import { getBuildTree } from "@/apis/bookManage"; import { getBuildTree } from "@/apis/bookManage";
// data undefined
const data: any = ref(); const data: any = ref();
// dialogVisible false
const dialogVisible = ref(false); const dialogVisible = ref(false);
// drawerProps DialogProps undefined
const drawerProps = ref<DialogProps>(); const drawerProps = ref<DialogProps>();
// DialogProps
// treeApi PromisePromise
interface DialogProps { interface DialogProps {
treeApi: (params: any) => Promise<any>; treeApi: (params: any) => Promise<any>;
} }
// defaultProps el-tree
// id: "id" "id"
// label: "name" "name"
// children: "childrenList" "childrenList"
const defaultProps = { const defaultProps = {
id: "id", id: "id",
label: "name", label: "name",
children: "childrenList" children: "childrenList"
}; };
// // treeAcceptParams
const treeAcceptParams = async (params: DialogProps) => { const treeAcceptParams = async (params: DialogProps) => {
// drawerProps
drawerProps.value = params; drawerProps.value = params;
//
dialogVisible.value = true; dialogVisible.value = true;
// getBuildTree
const res: any = await getBuildTree(); const res: any = await getBuildTree();
// data
data.value = res.data; data.value = res.data;
}; };
// // treeAcceptParams 便
defineExpose({ defineExpose({
treeAcceptParams treeAcceptParams
}); });
// const emit = defineEmits(['getCheckElderInfo']) // "getCheckBedInfo"
//
const emit = defineEmits<{ const emit = defineEmits<{
(event: "getCheckBedInfo", val: any): void (event: "getCheckBedInfo", val: any): void
}>(); }>();
//
// checkBed
const checkBed = (bed: any) => { const checkBed = (bed: any) => {
// level 4
if (bed.level === 4) { if (bed.level === 4) {
// "getCheckBedInfo"
emit("getCheckBedInfo", bed); emit("getCheckBedInfo", bed);
//
dialogVisible.value = false; dialogVisible.value = false;
} }
}; };

@ -1,4 +1,14 @@
<template> <template>
<!-- Element Plus 的上传组件 el-upload -->
<!-- v-model:file-list="imageList" 双向绑定上传文件列表imageList 是一个响应式变量 -->
<!-- :action="requestUrl" 设置上传文件的接口地址requestUrl 是一个变量 -->
<!-- :headers="{ token: token }" 设置上传请求的头部信息包含 token -->
<!-- list-type="picture-card" 设置上传文件列表的展示类型为图片卡片形式 -->
<!-- :before-upload="uploadBefore" 在文件上传前调用 uploadBefore 函数进行处理 -->
<!-- :on-error="handleError" 当文件上传失败时调用 handleError 函数 -->
<!-- :on-success="handleSuccess" 当文件上传成功时调用 handleSuccess 函数 -->
<!-- :on-preview="handlePreview" 当点击预览图片时调用 handlePreview 函数 -->
<!-- :on-remove="handleRemove" 当移除上传文件时调用 handleRemove 函数 -->
<el-upload <el-upload
v-model:file-list="imageList" v-model:file-list="imageList"
:action="requestUrl" :action="requestUrl"
@ -10,12 +20,16 @@
:on-preview="handlePreview" :on-preview="handlePreview"
:on-remove="handleRemove" :on-remove="handleRemove"
> >
<!-- Element Plus 的图标组件 el-icon展示一个加号图标 -->
<el-icon> <el-icon>
<Plus /> <Plus />
</el-icon> </el-icon>
</el-upload> </el-upload>
<!-- Element Plus 的对话框组件 el-dialog用于展示预览图片 -->
<!-- v-model="dialogVisible" 双向绑定对话框的显示隐藏状态dialogVisible 是一个响应式变量 -->
<el-dialog v-model="dialogVisible"> <el-dialog v-model="dialogVisible">
<!-- img 标签用于显示预览图片w-full 可能是一个自定义的类名表示宽度占满:src="dialogImageUrl" 绑定图片的源地址dialogImageUrl 是一个响应式变量 -->
<img w-full :src="dialogImageUrl" alt="Preview Image" /> <img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog> </el-dialog>
</template> </template>
@ -50,97 +64,126 @@ uploadParams.value.imageList.push({
--> -->
<script lang="ts" setup> <script lang="ts" setup>
// Vuex store
import store from "@/store"; import store from "@/store";
// URL
import { baseUrl } from "@/utils/http"; import { baseUrl } from "@/utils/http";
// Element Plus ElMessage
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
// Element Plus Plus
import { Plus } from "@element-plus/icons-vue"; import { Plus } from "@element-plus/icons-vue";
// Vue
import { defineEmits, onMounted, ref, watch } from "vue"; import { defineEmits, onMounted, ref, watch } from "vue";
// Element Plus
import type { UploadUserFile, ElUpload } from "element-plus"; import type { UploadUserFile, ElUpload } from "element-plus";
// page data // dialogImageUrl URL
const dialogImageUrl = ref(""); const dialogImageUrl = ref("");
// dialogVisible false
const dialogVisible = ref(false); const dialogVisible = ref(false);
// imageList
const imageList = ref<UploadUserFile[]>([]); const imageList = ref<UploadUserFile[]>([]);
// father component transfer to this date // props uploadParams
const props = defineProps({ const props = defineProps({
uploadParams: Object uploadParams: Object
}); });
// //
onMounted(() => { onMounted(() => {
// uploadParams imageList imageList
props.uploadParams?.["imageList"].forEach((image: any) => { props.uploadParams?.["imageList"].forEach((image: any) => {
imageList.value.push(image); imageList.value.push(image);
}); });
}); });
// http request data // URL
const requestUrl = baseUrl + "file/uploadImg"; const requestUrl = baseUrl + "file/uploadImg";
// Vuex store token
const token = store.state.app.token; const token = store.state.app.token;
// return data // imageUrlList URL
const imageUrlList = ref<any[]>([]); const imageUrlList = ref<any[]>([]);
// // "setImageData"
const emits = defineEmits(["setImageData"]); const emits = defineEmits(["setImageData"]);
// // updateData
const updateData = () => { const updateData = () => {
// // imageUrlList
imageUrlList.value = []; imageUrlList.value = [];
// imageList URL imageUrlList
imageList.value.forEach(image => imageUrlList.value.push(image.url)); imageList.value.forEach(image => imageUrlList.value.push(image.url));
// // data
const data = ref<any>(); const data = ref<any>();
//
if (props.uploadParams?.["uploadType"] === "single") { if (props.uploadParams?.["uploadType"] === "single") {
// URL data
data.value = imageUrlList.value[0]; data.value = imageUrlList.value[0];
} else { } else {
// imageUrlList data
data.value = imageUrlList; data.value = imageUrlList;
} }
// // "setImageData"
emits("setImageData", data); emits("setImageData", data);
}; };
// // imageList
watch(imageList, (value, oldValue, onCleanup) => { watch(imageList, (value, oldValue, onCleanup) => {
// imageList 1
if (props.uploadParams?.["uploadType"] === "single" && imageList.value.length > 1) { if (props.uploadParams?.["uploadType"] === "single" && imageList.value.length > 1) {
//
imageList.value.splice(0, 1); imageList.value.splice(0, 1);
//
updateData(); updateData();
} }
}); });
// // uploadBefore
const uploadBefore = (file: any) => { const uploadBefore = (file: any) => {
// jpeg png
const isJPG = file.type === "image/jpeg" || file.type === "image/png"; const isJPG = file.type === "image/jpeg" || file.type === "image/png";
// 2MB
const isLt2M = file.size / 1024 / 1024 < 2; const isLt2M = file.size / 1024 / 1024 < 2;
// jpeg png
if (!isJPG) { if (!isJPG) {
ElMessage.error("只能上传jpg/png文件"); ElMessage.error("只能上传jpg/png文件");
} }
// 2MB
if (!isLt2M) { if (!isLt2M) {
ElMessage.error("文件大小不能超过2MB"); ElMessage.error("文件大小不能超过2MB");
} }
//
return isJPG && isLt2M; return isJPG && isLt2M;
}; };
// // handleError
const handleError = (response: any) => { const handleError = (response: any) => {
//
ElMessage.error(response.data.msg); ElMessage.error(response.data.msg);
}; };
// // handleSuccess
const handleSuccess = (response: any, uploadFile: any) => { const handleSuccess = (response: any, uploadFile: any) => {
// URL url
uploadFile.url = response.data.url; uploadFile.url = response.data.url;
//
updateData(); updateData();
}; };
// // handleRemove
const handleRemove = async (uploadFile: any) => { const handleRemove = async (uploadFile: any) => {
imageList.value.filter((image) => image.uid !== uploadFile.uid); // imageList
imageList.value.filter((image) => image.uid!== uploadFile.uid);
//
updateData(); updateData();
// TODO imageId : uploadFile.uid // TODO imageId : uploadFile.uid
}; };
// // handlePreview
const handlePreview = (uploadFile: any) => { const handlePreview = (uploadFile: any) => {
// URL URL
dialogImageUrl.value = uploadFile.url!; dialogImageUrl.value = uploadFile.url!;
//
dialogVisible.value = true; dialogVisible.value = true;
}; };
</script> </script>

@ -1,28 +1,36 @@
<!-- Element Plus 中动态添加组件可以使用 el-component 组件该组件可以将任何一个 Vue 组件动态添加到页面中下面是一个示例代码-->
<!--在上面的示例中我们使用了 el-component 组件来动态添加组件通过 :is 属性指定要添加的组件类型通过 :props 属性传递组件所需的 props 数据-->
<!-- addNew 方法中我们向 componentList 数组中添加一个组件对象其中 type 属性指定要添加的组件类型props 属性指定要传递给组件的数据-->
<!--最后在模板中使用 v-for 指令遍历 componentList 数组并通过 :is :props 属性将组件动态添加到页面中-->
<!--需要注意的是在使用 el-component 组件动态添加组件时需要将要添加的组件在 components 中先进行注册-->
<template> <template>
<div> <div>
<!-- 定义一个 Element Plus 的按钮点击该按钮会触发 addNew 方法 -->
<el-button @click="addNew"></el-button> <el-button @click="addNew"></el-button>
<!-- 使用 v-for 指令遍历 componentList 数组将数组中的每个元素渲染为一个组件 -->
<div v-for="(item, index) in componentList" :key="index"> <div v-for="(item, index) in componentList" :key="index">
<!-- 使用 el-component 动态添加组件 -->
<!-- :is="item.type" 指定要渲染的组件类型该类型从 componentList 数组元素的 type 属性获取 -->
<!-- :props="item.props" 传递组件所需的 props 数据这些数据从 componentList 数组元素的 props 属性获取 -->
<el-component :is="item.type" :props="item.props"></el-component> <el-component :is="item.type" :props="item.props"></el-component>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
// HelloWorld
import HelloWorld from './components/HelloWorld.vue' import HelloWorld from './components/HelloWorld.vue'
export default { export default {
// data
data() { data() {
return { return {
// componentList
componentList: [] componentList: []
} }
}, },
// methods
methods: { methods: {
// addNew componentList
addNew() { addNew() {
// componentList
// type HelloWorld
// props HelloWorld msg 'Hello World!'
this.componentList.push({ this.componentList.push({
type: HelloWorld, type: HelloWorld,
props: { props: {
@ -31,6 +39,7 @@ export default {
}) })
} }
}, },
// components HelloWorld
components: { components: {
HelloWorld HelloWorld
} }

@ -1,27 +1,42 @@
// 定义一个名为 Table 的命名空间
export namespace Table { export namespace Table {
// 在 Table 命名空间内,定义一个接口 Pageable用于描述分页相关的属性
export interface Pageable { export interface Pageable {
// 当前页码,类型为数字
pageNum: number pageNum: number
// 每页显示的数量,类型为数字
pageSize: number pageSize: number
// 数据的总数量,类型为数字
total: number total: number
} }
// 在 Table 命名空间内,定义一个接口 TableStateProps用于描述表格相关的状态属性
export interface TableStateProps { export interface TableStateProps {
// 表格数据,是一个任意类型的数组
tableData: any[] tableData: any[]
// 分页相关的属性,类型为 Pageable 接口
pageable: Pageable pageable: Pageable
// 搜索参数,是一个键值对对象,键为字符串,值为任意类型
searchParam: { searchParam: {
[key: string]: any [key: string]: any
} }
// 初始化的搜索参数,是一个键值对对象,键为字符串,值为任意类型
searchInitParam: { searchInitParam: {
[key: string]: any [key: string]: any
} }
// 用于获取总数的参数,是一个键值对对象,键为字符串,值为任意类型
totalParam: { totalParam: {
[key: string]: any [key: string]: any
} }
// 可选的图标相关属性,是一个键值对对象,键为字符串,值为任意类型,默认为 undefined
icon?: { icon?: {
[key: string]: any [key: string]: any
} }
} }
} }
// 定义一个名为 HandleData 的命名空间
export namespace HandleData { export namespace HandleData {
export type MessageType = '' | 'success' | 'warning' | 'info' | 'error' // 在 HandleData 命名空间内,定义一个类型别名 MessageType
// MessageType 的值可以是空字符串,或者是'success'、'warning'、'info'、'error' 中的一个
export type MessageType = '' |'success' | 'warning' | 'info' | 'error'
} }

@ -1,4 +1,6 @@
// 从 element-plus 库中导入 ElMessageBox 和 ElMessage 组件,分别用于显示确认框和提示消息
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
// 从 './interface' 文件中导入 HandleData 命名空间,用于获取其中定义的类型
import { HandleData } from './interface' import { HandleData } from './interface'
/** /**
@ -9,26 +11,42 @@ import { HandleData } from './interface'
* @param {String} confirmType icon(, warning) * @param {String} confirmType icon(, warning)
* @return Promise * @return Promise
*/ */
// 定义一个泛型函数 useHandleDataP 表示 api 函数参数的类型R 表示 api 函数返回值的类型
export const useHandleData = <P = any, R = any>( export const useHandleData = <P = any, R = any>(
// api 是一个函数,接收类型为 P 的参数并返回一个 PromisePromise 的解析值类型为 R
api: (params: P) => Promise<R>, api: (params: P) => Promise<R>,
// params 是 api 函数的参数,类型与 api 函数的参数类型一致
params: Parameters<typeof api>[0], params: Parameters<typeof api>[0],
// message 是一个字符串,用于显示确认框中的提示信息
message: string, message: string,
// confirmType 是一个字符串,类型为 HandleData.MessageType默认值为 'warning'
confirmType: HandleData.MessageType = 'warning' confirmType: HandleData.MessageType = 'warning'
) => { ) => {
// 返回一个 Promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 显示一个确认框
ElMessageBox.confirm(`是否${message}?`, '温馨提示', { ElMessageBox.confirm(`是否${message}?`, '温馨提示', {
// 确认按钮的文本
confirmButtonText: '确定', confirmButtonText: '确定',
// 取消按钮的文本
cancelButtonText: '取消', cancelButtonText: '取消',
// 确认框的图标类型
type: confirmType, type: confirmType,
// 是否可拖动
draggable: true draggable: true
}).then(async () => {
const res = await api(params)
if (!res) return reject(false)
ElMessage({
type: 'success',
message: `${message}成功!`
})
resolve(true)
}) })
.then(async () => {
// 调用 api 函数并等待其返回结果
const res = await api(params)
// 如果结果为 falsy 值,则 reject 该 Promise
if (!res) return reject(false)
// 显示一个成功提示消息
ElMessage({
type:'success',
message: `${message}成功!`
})
// resolve 该 Promise表示操作成功
resolve(true)
})
}) })
} }

@ -1,10 +1,21 @@
// 从 '@/components/SvgIcon/index.vue' 导入 SvgIcon 组件
import SvgIcon from '@/components/SvgIcon/index.vue' import SvgIcon from '@/components/SvgIcon/index.vue'
// 从 'vue' 导入 h 函数和 defineComponent 函数
import { h, defineComponent } from 'vue' import { h, defineComponent } from 'vue'
// 定义一个名为 useRenderIcon 的函数它接受两个参数iconName字符串类型和 attrs可选的任意类型
export function useRenderIcon(iconName: string, attrs?: any) { export function useRenderIcon(iconName: string, attrs?: any) {
// 使用 defineComponent 函数定义一个新的组件
return defineComponent({ return defineComponent({
// 新组件的名称为 'SvgIcon'
name: 'SvgIcon', name: 'SvgIcon',
// 定义组件的渲染函数
render() { render() {
// 使用 h 函数创建一个 SvgIcon 组件的虚拟节点
// 第一个参数是要创建的组件(这里是 SvgIcon
// 第二个参数是一个对象,包含要传递给 SvgIcon 组件的属性
// 'icon' 属性的值是传入的 iconName
// '...attrs' 表示将 attrs 对象中的所有属性也传递给 SvgIcon 组件
return h(SvgIcon, { return h(SvgIcon, {
icon: iconName, icon: iconName,
...attrs ...attrs

@ -4,16 +4,24 @@ import { ref, computed } from 'vue'
* @description * @description
* @param {String} rowKey id * @param {String} rowKey id
* */ * */
// 定义一个名为 useSelection 的函数,用于处理表格多选数据的操作
// 接受一个可选参数 rowKey默认值为 'id',表示行数据中用于唯一标识的键名
export const useSelection = (rowKey = 'id') => { export const useSelection = (rowKey = 'id') => {
// 是否选中数据 // 创建一个响应式变量 isSelected用于表示是否选中了数据
// 初始值为 false类型为 boolean
const isSelected = ref<boolean>(false) const isSelected = ref<boolean>(false)
// 选中的数据列表 // 创建一个响应式变量 selectedList用于存储选中的数据列表
// 初始值为空数组,类型为 any[]
const selectedList = ref([]) const selectedList = ref([])
// 当前选中的所有ids(数组)可根据项目自行配置id字段 // 创建一个计算属性 selectedListIds用于获取当前选中的所有数据的 id 数组
// 计算属性会根据依赖项(这里是 selectedList的变化而自动更新
const selectedListIds = computed((): string[] => { const selectedListIds = computed((): string[] => {
// 初始化一个空数组 ids 用于存储 id
const ids: string[] = [] const ids: string[] = []
// 遍历 selectedList 中的每一项数据
selectedList.value.forEach(item => ids.push(item[rowKey])) selectedList.value.forEach(item => ids.push(item[rowKey]))
// 返回包含所有 id 的数组
return ids return ids
}) })
@ -22,11 +30,18 @@ export const useSelection = (rowKey = 'id') => {
* @param {Array} rowArr * @param {Array} rowArr
* @return void * @return void
*/ */
// 定义一个函数 selectionChange用于处理表格多选数据的变化事件
// 接受一个参数 rowArr类型为 any[],表示当前选中的所有数据
const selectionChange = (rowArr: any) => { const selectionChange = (rowArr: any) => {
rowArr.length === 0 ? (isSelected.value = false) : (isSelected.value = true) // 如果选中的数据列表为空,则将 isSelected 设置为 false
selectedList.value = rowArr // 否则将 isSelected 设置为 true
} rowArr.length === 0? (isSelected.value = false) : (isSelected.value = true)
// 更新 selectedList 为当前选中的数据列表
selectedList.value = rowArr
}
// 返回一个包含 isSelected、selectedList、selectedListIds 和 selectionChange 的对象
// 以便在其他地方使用这些变量和函数来处理表格多选数据
return { return {
isSelected, isSelected,
selectedList, selectedList,

@ -8,160 +8,196 @@ import { reactive, computed, toRefs } from 'vue'
* @param {Boolean} isPageable (true) * @param {Boolean} isPageable (true)
* @param {Function} dataCallBack () * @param {Function} dataCallBack ()
* */ * */
// 定义一个名为 useTable 的函数,用于封装表格页面的操作方法
// api 是获取表格数据的 API 方法,是必传参数
// initParam 是获取数据的初始化参数,非必传,默认值为空对象
// isPageable 表示是否有分页功能,非必传,默认值为 true
// dataCallBack 是对后台返回的数据进行处理的方法,非必传
export const useTable = ( export const useTable = (
api: (params: any) => Promise<any>, api: (params: any) => Promise<any>,
initParam: object = {}, initParam: object = {},
isPageable = true, isPageable = true,
dataCallBack?: (data: any) => any dataCallBack?: (data: any) => any
) => { ) => {
// 使用 reactive 创建一个响应式的 state 对象,包含表格数据、分页数据、查询参数等
const state = reactive<Table.TableStateProps>({ const state = reactive<Table.TableStateProps>({
// 表格数据 // 表格数据,初始值为空数组
tableData: [], tableData: [],
// 分页数据 // 分页数据,包含当前页数、每页显示条数和总条数
pageable: { pageable: {
// 当前页数 // 当前页数,初始值为 1
pageNum: 1, pageNum: 1,
// 每页显示条数 // 每页显示条数,初始值为 10
pageSize: 10, pageSize: 10,
// 总条数 // 总条数,初始值为 0
total: 0 total: 0
}, },
// 查询参数(只包括查询) // 查询参数,只包括查询条件,初始值为空对象
searchParam: {}, searchParam: {},
// 初始化默认的查询参数 // 初始化默认的查询参数,初始值为空对象
searchInitParam: {}, searchInitParam: {},
// 总参数(包含分页和查询参数) // 总参数,包含分页和查询参数,初始值为空对象
totalParam: {} totalParam: {}
}) })
/** /**
* @description (,) * @description (,)
* */ * */
// 定义一个计算属性 pageParam用于获取和设置分页查询参数
const pageParam = computed({ const pageParam = computed({
get: () => { // 获取分页查询参数
return { get: () => {
pageNum: state.pageable.pageNum, return {
pageSize: state.pageable.pageSize pageNum: state.pageable.pageNum,
pageSize: state.pageable.pageSize
}
},
// 设置分页查询参数,这里暂时为空,可根据需要实现
set: (newVal: any) => { // 我是分页更新之后的值
} }
}, })
set: (newVal: any) => { // 我是分页更新之后的值
}
})
/** /**
* @description * @description
* @return void * @return void
* */ * */
// 定义一个异步函数 getTableList用于获取表格数据
const getTableList = async () => { const getTableList = async () => {
try { try {
// 先把初始化参数和分页参数放到总参数里面 // 先把初始化参数和分页参数放到总参数里面
Object.assign( Object.assign(
state.totalParam, state.totalParam,
initParam, initParam,
isPageable ? pageParam.value : {} isPageable? pageParam.value : {}
) )
//请求前格式化数据 // 请求前格式化数据,如果总参数中有 consultDate 字段,则进行处理
if (state.totalParam.consultDate) { if (state.totalParam.consultDate) {
state.totalParam.startTime = state.totalParam.consultDate[0] state.totalParam.startTime = state.totalParam.consultDate[0]
state.totalParam.endTime = state.totalParam.consultDate[1] state.totalParam.endTime = state.totalParam.consultDate[1]
delete state.totalParam.consultDate delete state.totalParam.consultDate
} }
let { data } = await api({ // 调用 API 方法获取数据
...state.searchInitParam, let { data } = await api({
...state.totalParam ...state.searchInitParam,
}) ...state.totalParam
dataCallBack && (data = dataCallBack(data)) })
// 获取当前表格数据 // 如果有数据处理回调函数,则对数据进行处理
state.tableData = isPageable ? data.list : data dataCallBack && (data = dataCallBack(data))
// 获取当前表格数据,如果有分页,则取 data.list否则取 data
state.tableData = isPageable? data.list : data
const { pageNum, pageSize, total } = data // 从返回的数据中获取分页信息
isPageable && updatePageable({ pageNum, pageSize, total }) const { pageNum, pageSize, total } = data
} catch (error) { // 如果有分页,则更新分页信息
console.log(error) isPageable && updatePageable({ pageNum, pageSize, total })
} catch (error) {
// 如果发生错误,打印错误信息
console.log(error)
}
} }
}
/** /**
* @description * @description
* @return void * @return void
* */ * */
// 定义一个函数 updatedTotalParam用于更新总参数
const updatedTotalParam = () => { const updatedTotalParam = () => {
state.totalParam = {} state.totalParam = {}
// 处理查询参数,可以给查询参数加自定义前缀操作 // 处理查询参数,可以给查询参数加自定义前缀操作
const nowSearchParam: { [key: string]: any } = {} const nowSearchParam: { [key: string]: any } = {}
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀) // 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
for (const key in state.searchParam) { for (const key in state.searchParam) {
// * 某些情况下参数为 false/0 也应该携带参数 // * 某些情况下参数为 false/0 也应该携带参数
if ( if (
state.searchParam[key] || state.searchParam[key] ||
state.searchParam[key] === false || state.searchParam[key] === false ||
state.searchParam[key] === 0 state.searchParam[key] === 0
) { ) {
nowSearchParam[key] = state.searchParam[key] nowSearchParam[key] = state.searchParam[key]
}
} }
// 将处理后的查询参数和分页参数合并到总参数中
Object.assign(
state.totalParam,
nowSearchParam,
isPageable? pageParam.value : {}
)
} }
Object.assign(
state.totalParam,
nowSearchParam,
isPageable ? pageParam.value : {}
)
}
/** /**
* @description * @description
* @param {Object} resPageable * @param {Object} resPageable
* @return void * @return void
* */ * */
// 定义一个函数 updatePageable用于更新分页信息
const updatePageable = (resPageable: Table.Pageable) => { const updatePageable = (resPageable: Table.Pageable) => {
Object.assign(state.pageable, resPageable) // 使用 Object.assign 方法将后台返回的分页数据合并到 state.pageable 中
} Object.assign(state.pageable, resPageable)
}
/** /**
* @description * @description
* @return void * @return void
* */ * */
// 定义一个函数 search用于进行表格数据查询
const search = () => { const search = () => {
state.pageable.pageNum = 1 // 将当前页数设置为 1
updatedTotalParam() state.pageable.pageNum = 1
getTableList() // 更新总参数
} updatedTotalParam()
// 获取表格数据
getTableList()
}
/** /**
* @description * @description
* @return void * @return void
* */ * */
// 定义一个函数 reset用于重置表格数据
const reset = () => { const reset = () => {
state.pageable.pageNum = 1 // 将当前页数设置为 1
state.searchParam = {} state.pageable.pageNum = 1
// 重置搜索表单的时,如果有默认搜索参数,则重置默认的搜索参数 // 清空搜索参数
Object.keys(state.searchInitParam).forEach(key => { state.searchParam = {}
state.searchParam[key] = state.searchInitParam[key] // 重置搜索表单时,如果有默认搜索参数,则重置为默认值
}) Object.keys(state.searchInitParam).forEach(key => {
updatedTotalParam() state.searchParam[key] = state.searchInitParam[key]
getTableList() })
} // 更新总参数
updatedTotalParam()
// 获取表格数据
getTableList()
}
/** /**
* @description * @description
* @param {Number} val * @param {Number} val
* @return void * @return void
* */ * */
// 定义一个函数 handleSizeChange用于处理每页条数改变的事件
const handleSizeChange = (val: number) => { const handleSizeChange = (val: number) => {
state.pageable.pageNum = 1 // 将当前页数设置为 1
state.pageable.pageSize = val state.pageable.pageNum = 1
getTableList() // 更新每页显示条数
} state.pageable.pageSize = val
// 获取表格数据
getTableList()
}
/** /**
* @description * @description
* @param {Number} val * @param {Number} val
* @return void * @return void
* */ * */
// 定义一个函数 handleCurrentChange用于处理当前页改变的事件
const handleCurrentChange = (val: number) => { const handleCurrentChange = (val: number) => {
state.pageable.pageNum = val // 更新当前页数
getTableList() state.pageable.pageNum = val
} // 获取表格数据
getTableList()
}
// 返回一个包含 state 的响应式引用、获取表格数据的函数、查询函数、重置函数、处理每页条数改变和当前页改变的函数以及更新总参数的函数
return { return {
...toRefs(state), ...toRefs(state),
getTableList, getTableList,

@ -1,15 +1,21 @@
// 从项目路径 '@/components/SvgIcon/index.vue' 导入 SvgIcon 组件
import SvgIcon from '@/components/SvgIcon/index.vue' import SvgIcon from '@/components/SvgIcon/index.vue'
// 从 'vue' 中导入 App 类型,用于表示 Vue 应用实例
import { App } from 'vue' import { App } from 'vue'
// 获取上下文 require.context检索的目录是否检索子文件夹正则表达式 // 获取上下文 require.context检索的目录是否检索子文件夹正则表达式
// 返回值是一个函数(传入路径可以导入文件) // 返回值是一个函数(传入路径可以导入文件)
// 通过静态方法keys可以检索所有文件路径 // 通过静态方法keys可以检索所有文件路径
// 通过.prototype可以查看所有静态方法 // 通过.prototype可以查看所有静态方法
// 这里指定检索当前目录下的 './svg' 文件夹,不检索子文件夹,匹配所有以.svg 结尾的文件
const svgRequired = require.context('./svg', false, /\.svg$/) const svgRequired = require.context('./svg', false, /\.svg$/)
// 遍历所有匹配的 SVG 文件路径
svgRequired.keys().forEach(item => svgRequired(item)) svgRequired.keys().forEach(item => svgRequired(item))
// 导出一个默认函数,该函数接收一个 Vue 应用实例 app 作为参数
export default (app: App) => { export default (app: App) => {
// 使用 app.component 方法将 SvgIcon 组件注册为全局组件,别名为'svg-icon'
// 这样在整个 Vue 应用中都可以使用 <svg-icon> 标签来使用该组件
app.component('svg-icon', SvgIcon) app.component('svg-icon', SvgIcon)
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

@ -0,0 +1,109 @@
<template>
<!-- 根元素 div通过 style 绑定动态样式v-show 根据 isShow 的值控制元素的显示与隐藏 -->
<div :style="style" v-show="isShow">
<!-- 插槽用于在父组件中插入内容 -->
<slot></slot>
</div>
</template>
<script setup lang="ts" name="GridItem">
// vue
import { computed, inject, Ref, ref, useAttrs, watch } from 'vue'
// '../interface/index' BreakPoint Responsive
import { BreakPoint, Responsive } from '../interface/index'
// Props
type Props = {
// 0
offset?: number
// 1
span?: number
// false
suffix?: boolean
// xs undefined
xs?: Responsive
// sm undefined
sm?: Responsive
// md undefined
md?: Responsive
// lg undefined
lg?: Responsive
// xl undefined
xl?: Responsive
}
// 使 withDefaults defineProps
const props = withDefaults(defineProps<Props>(), {
offset: 0,
span: 1,
suffix: false,
xs: undefined,
sm: undefined,
md: undefined,
lg: undefined,
xl: undefined
})
// props
const attrs = useAttrs() as any
// isShow true
const isShow = ref(true)
// breakPoint Ref<BreakPoint> ref('xl')
const breakPoint = inject<Ref<BreakPoint>>('breakPoint', ref('xl'))
// shouldHiddenIndex Ref<number> ref(-1)
const shouldHiddenIndex = inject<Ref<number>>('shouldHiddenIndex', ref(-1))
// shouldHiddenIndex breakPoint
watch(
// shouldHiddenIndex.value breakPoint.value
() => [shouldHiddenIndex.value, breakPoint.value],
// n shouldHiddenIndex.value breakPoint.value
n => {
// attrs index
if (attrs.index) {
//
isShow.value =!(n[0]!== -1 && parseInt(attrs.index) >= n[0])
}
},
// immediate: true
{ immediate: true }
)
// gap 0
const gap = inject('gap', 0)
// cols Ref<number> ref(4)
const cols = inject<Ref<number>>('cols', ref(4))
// style props
const style = computed(() => {
// span 使 props.span
let span = props[breakPoint.value]?.span?? props.span
// offset 使 props.offset
let offset = props[breakPoint.value]?.offset?? props.offset
//
if (props.suffix) {
return {
// gridColumnStart
gridColumnStart: cols.value - span - offset + 1,
// gridColumnEnd
gridColumnEnd: `span ${span + offset}`,
// marginLeft
marginLeft:
offset!== 0
? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})`
: 'unset'
}
} else {
return {
// gridColumn
gridColumn: `span ${
span + offset > cols.value? cols.value : span + offset
}/span ${span + offset > cols.value? cols.value : span + offset}`,
// marginLeft
marginLeft:
offset!== 0
? `calc(((100% + ${gap}px) / ${span + offset}) * ${offset})`
: 'unset'
}
}
})
</script>

@ -0,0 +1,207 @@
<template>
<!-- 根元素 div通过 style 绑定动态样式用于包裹插槽内容 -->
<div :style="style">
<!-- 插槽用于父组件插入内容 -->
<slot></slot>
</div>
</template>
<script setup lang="ts" name="Grid">
// 'vue' Vue
import {
ref,
watch,
useSlots,
computed,
provide,
onBeforeMount,
onMounted,
onUnmounted,
onDeactivated,
onActivated,
VNodeArrayChildren,
VNode
} from 'vue'
// BreakPoint
import type { BreakPoint } from './interface/index'
// Props
type Props = {
//
cols?: number | Record<BreakPoint, number>
// false
collapsed?: boolean
// 1
collapsedRows?: number
// [, ]
gap?: [number, number] | number
}
// 使 withDefaults
const props = withDefaults(defineProps<Props>(), {
//
cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
//
collapsed: false,
// 1
collapsedRows: 1,
// 0
gap: 0
})
// collapsed true findIndex
onBeforeMount(() => props.collapsed && findIndex())
// resize window resize
onMounted(() => {
resize({ target: { innerWidth: window.innerWidth } } as any)
window.addEventListener('resize', resize)
})
// resize window resize
onActivated(() => {
resize({ target: { innerWidth: window.innerWidth } } as any)
window.addEventListener('resize', resize)
})
// window resize
onUnmounted(() => {
window.removeEventListener('resize', resize)
})
// window resize
onDeactivated(() => {
window.removeEventListener('resize', resize)
})
// resize breakPoint
const resize = (e: UIEvent) => {
//
let width = (e.target as Window).innerWidth
// breakPoint
switch (!!width) {
case width < 768:
breakPoint.value = 'xs'
break
case width >= 768 && width < 992:
breakPoint.value ='sm'
break
case width >= 992 && width < 1200:
breakPoint.value ='md'
break
case width >= 1200 && width < 1920:
breakPoint.value = 'lg'
break
case width >= 1920:
breakPoint.value = 'xl'
break
}
}
// gap gap 使 gap
provide('gap', Array.isArray(props.gap)? props.gap[0] : props.gap)
// breakPoint
let breakPoint = ref<BreakPoint>('xl')
provide('breakPoint', breakPoint)
// index
const hiddenIndex = ref(-1)
provide('shouldHiddenIndex', hiddenIndex)
// cols cols breakPoint 使 cols
const cols = computed(() => {
if (typeof props.cols === 'object')
return props.cols[breakPoint.value]?? props.cols
return props.cols
})
provide('cols', cols)
//
const slots = useSlots().default!()
// findIndex index
const findIndex = () => {
//
let fields: VNodeArrayChildren = []
//
let suffix: any = null
//
slots.forEach((slot: any) => {
// 'GridItem' suffix
if (
typeof slot.type === 'object' &&
slot.type.name === 'GridItem' &&
slot.props?.suffix!== undefined
)
suffix = slot
// symbol
if (typeof slot.type ==='symbol' && Array.isArray(slot.children))
slot.children.forEach((child: any) => fields.push(child))
})
// suffix
let suffixCols = 0
if (suffix) {
suffixCols =
(suffix.props![breakPoint.value]?.span?? suffix.props?.span?? 1) +
(suffix.props![breakPoint.value]?.offset?? suffix.props?.offset?? 0)
}
try {
//
let find = false
//
fields.reduce((prev = 0, current, index) => {
prev +=
((current as VNode)!.props![breakPoint.value]?.span??
(current as VNode)!.props?.span??
1) +
((current as VNode)!.props![breakPoint.value]?.offset??
(current as VNode)!.props?.offset??
0)
if ((prev as number) > props.collapsedRows * cols.value - suffixCols) {
hiddenIndex.value = index
find = true
throw 'find it'
}
return prev
}, 0)
// hiddenIndex -1
if (!find) hiddenIndex.value = -1
} catch (e) {
// console.warn(e);
}
}
// breakPoint collapsed true findIndex
watch(
() => breakPoint.value,
() => {
if (props.collapsed) findIndex()
}
)
// collapsed true findIndex hiddenIndex -1
watch(
() => props.collapsed,
value => {
if (value) return findIndex()
hiddenIndex.value = -1
}
)
//
const gap = computed(() => {
if (typeof props.gap === 'number') return `${props.gap}px`
if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`
return 'unset'
})
//
const style = computed(() => {
return {
display: 'grid',
gridGap: gap.value,
gridTemplateColumns: `repeat(${cols.value}, minmax(0, 1fr))`
}
})
// breakPoint便访
defineExpose({ breakPoint })
</script>

@ -0,0 +1,12 @@
// 定义一个类型别名 BreakPoint它的值只能是 'xs'、'sm'、'md'、'lg' 或 'xl' 中的一个,
// 通常用于表示响应式布局中的不同屏幕断点
export type BreakPoint = 'xs' |'sm' |'md' | 'lg' | 'xl';
// 定义一个类型别名 Responsive它是一个对象类型包含两个可选属性 span 和 offset。
// span 用于表示在响应式布局中元素所占的列数,类型为 number 类型的可选值。
// offset 用于表示在响应式布局中元素的偏移量,类型为 number 类型的可选值。
// 这个类型通常用于描述在不同屏幕断点下元素的布局属性
export type Responsive = {
span?: number;
offset?: number;
};

@ -0,0 +1,45 @@
<template>
<!-- Component Vue 中的动态组件:is="icon" 表示根据 icon 的值来动态渲染对应的图标组件 -->
<!-- :theme="theme" 绑定图标主题如轮廓outline填充filled -->
<!-- :size="size" 绑定图标大小可以是数字或字符串类型 -->
<!-- :spin="spin" 绑定图标是否旋转的状态布尔类型 -->
<!-- :fill="fill" 绑定图标的填充颜色可以是字符串或字符串数组 -->
<!-- :strokeLinecap="strokeLinecap" 绑定图标描边端点的样式 -->
<!-- :strokeLinejoin="strokeLinejoin" 绑定图标描边连接处的样式 -->
<!-- :strokeWidth="strokeWidth" 绑定图标描边的宽度 -->
<Component
:is="icon"
:theme="theme"
:size="size"
:spin="spin"
:fill="fill"
:strokeLinecap="strokeLinecap"
:strokeLinejoin="strokeLinejoin"
:strokeWidth="strokeWidth"
/>
</template>
<script setup lang="ts">
// '@icon-park/vue-next/lib/runtime' Icon
import type { Icon } from '@icon-park/vue-next/lib/runtime'
// 使 defineProps
defineProps<{
// icon Icon
icon: Icon
// theme 'outline' | 'filled' | 'two-tone' |'multi-color'
theme?: 'outline' | 'filled' | 'two-tone' |'multi-color'
// size number | string
size?: number | string
// spin boolean
spin?: boolean
// fill string | string[]
fill?: string | string[]
// strokeLinecap 'butt' | 'round' |'square'
strokeLinecap?: 'butt' | 'round' |'square'
// strokeLinejoin 'miter' | 'round' | 'bevel'
strokeLinejoin?: 'miter' | 'round' | 'bevel'
// strokeWidth number
strokeWidth?: number
}>()
</script>

@ -1,34 +1,49 @@
<template> <template>
<!-- Element Plus 的下拉菜单组件触发方式为点击 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<!-- 下拉菜单的触发元素 -->
<span class="navbar-bg-hover"> <span class="navbar-bg-hover">
<!-- 显示用户头像图片地址通过 :src 绑定 avator 变量 -->
<img :src="avator" /> <img :src="avator" />
<!-- 如果用户名存在显示用户名使用了暗色模式下文字颜色为黑色的样式 -->
<p v-if="username" class="dark:text-black">{{ username }}</p> <p v-if="username" class="dark:text-black">{{ username }}</p>
</span> </span>
<!-- 下拉菜单内容模板 -->
<template #dropdown> <template #dropdown>
<!-- Element Plus 的下拉菜单 -->
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">
<!-- 下拉菜单项点击触发 editPassShow 方法 -->
<el-dropdown-item @click="editPassShow"> <el-dropdown-item @click="editPassShow">
<!-- 自定义的 SVG 图标组件显示修改密码图标 -->
<svg-icon <svg-icon
@click="editPassShow" @click="editPassShow"
icon="verify" icon="verify"
size="14" size="14"
style="margin-right: 5px" style="margin-right: 5px"
></svg-icon> ></svg-icon>
<!-- 菜单项文本 -->
修改密码 修改密码
</el-dropdown-item> </el-dropdown-item>
<!-- 下拉菜单项点击触发 logout 方法 -->
<el-dropdown-item @click="logout"> <el-dropdown-item @click="logout">
<!-- 自定义的 SVG 图标组件显示退出登录图标 -->
<svg-icon <svg-icon
@click="logout" @click="logout"
icon="logout" icon="logout"
size="14" size="14"
style="margin-right: 5px" style="margin-right: 5px"
></svg-icon> ></svg-icon>
<!-- 菜单项文本 -->
退出登录 退出登录
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<!-- Element Plus 的对话框组件用于修改密码 -->
<el-dialog v-model="editPassVisible" title="修改密码"> <el-dialog v-model="editPassVisible" title="修改密码">
<!-- 对话框内容模板 -->
<template #default> <template #default>
<!-- Element Plus 的表单组件绑定表单数据表单规则和表单引用 -->
<el-form <el-form
:model="formData" :model="formData"
class="forget-pass-form" class="forget-pass-form"
@ -36,7 +51,9 @@
:rules="editPassRules" :rules="editPassRules"
size="large" size="large"
> >
<!-- 表单项对应旧密码字段 -->
<el-form-item prop="oldPass"> <el-form-item prop="oldPass">
<!-- Element Plus 的输入框组件绑定旧密码数据显示为密码输入框 -->
<el-input <el-input
v-model="formData.oldPass" v-model="formData.oldPass"
placeholder="旧密码" placeholder="旧密码"
@ -46,8 +63,9 @@
show-password show-password
/> />
</el-form-item> </el-form-item>
<!-- 表单项对应新密码字段 -->
<el-form-item prop="newPass"> <el-form-item prop="newPass">
<!-- Element Plus 的输入框组件绑定新密码数据显示为密码输入框 -->
<el-input <el-input
v-model="formData.newPass" v-model="formData.newPass"
placeholder="新密码" placeholder="新密码"
@ -57,8 +75,9 @@
show-password show-password
/> />
</el-form-item> </el-form-item>
<!-- 表单项对应确认密码字段 -->
<el-form-item prop="confirmPassword"> <el-form-item prop="confirmPassword">
<!-- Element Plus 的输入框组件绑定确认密码数据显示为密码输入框 -->
<el-input <el-input
v-model="formData.confirmPassword" v-model="formData.confirmPassword"
placeholder="确认密码" placeholder="确认密码"
@ -70,9 +89,12 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
<!-- 对话框底部模板 -->
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<!-- 取消按钮点击关闭对话框 -->
<el-button @click="editPassVisible = false">取消</el-button> <el-button @click="editPassVisible = false">取消</el-button>
<!-- 确认修改按钮点击触发 handleEditPass 方法显示加载状态 -->
<el-button <el-button
class="bg-blue" class="bg-blue"
type="primary" type="primary"
@ -87,43 +109,61 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// Vuex
import store from '@/store' import store from '@/store'
//
import { useRenderIcon } from '@/hooks/useIcons' import { useRenderIcon } from '@/hooks/useIcons'
// Vue
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
// API
import { editPass, forgetPass, getLogout, IEditPassImpl } from '@/apis/user' import { editPass, forgetPass, getLogout, IEditPassImpl } from '@/apis/user'
// Element Plus
import { ElMessage, FormInstance, FormRules } from 'element-plus' import { ElMessage, FormInstance, FormRules } from 'element-plus'
// Vuex
const avator = store.state.app.userPeofile.avator const avator = store.state.app.userPeofile.avator
// Vuex
const username = store.state.app.userPeofile.username const username = store.state.app.userPeofile.username
//
const editPassVisible = ref(false) const editPassVisible = ref(false)
//
const ruleFormRef = ref<FormInstance | null>(null) const ruleFormRef = ref<FormInstance | null>(null)
//
const loading = ref(false) const loading = ref(false)
//
const formData = ref({ const formData = ref({
oldPass: '12345', oldPass: '12345',
newPass: '123456', newPass: '123456',
confirmPassword: '123456' confirmPassword: '123456'
}) })
// //
const editPassShow = () => { const editPassShow = () => {
editPassVisible.value = true editPassVisible.value = true
} }
// //
const handleEditPass = (formRef: FormInstance | null) => { const handleEditPass = (formRef: FormInstance | null) => {
//
loading.value = true loading.value = true
//
if (!formRef) return if (!formRef) return
//
formRef.validate(async (valid, fields) => { formRef.validate(async (valid, fields) => {
if (valid) { if (valid) {
// API
// const res: any = await editPass({ // const res: any = await editPass({
// oldPass: formData.value.oldPass, // oldPass: formData.value.oldPass,
// newPass: formData.value.newPass // newPass: formData.value.newPass
// }) // })
// 使 API
const res: any = await editPass(new IEditPassImpl('1', '1')) const res: any = await editPass(new IEditPassImpl('1', '1'))
if (res.code === 200) { if (res.code === 200) {
// 退
logout() logout()
} else { } else {
//
loading.value = false loading.value = false
ElMessage({ ElMessage({
message: res.msg, message: res.msg,
@ -131,63 +171,84 @@ const handleEditPass = (formRef: FormInstance | null) => {
}) })
} }
} else { } else {
//
loading.value = false loading.value = false
return fields return fields
} }
}) })
} }
// 退 // 退
const logout = async () => { const logout = async () => {
// 退 API
await getLogout() await getLogout()
// Vuex 退
store.dispatch('app/logout') store.dispatch('app/logout')
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success' type: 'success'
}) })
} }
/* 修改密码校验 */ /* 修改密码校验规则 */
const editPassRules = reactive<FormRules>({ const editPassRules = reactive<FormRules>({
oldPass: [ oldPass: [
{ {
//
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
//
let oldPass = value?.trim() let oldPass = value?.trim()
if (oldPass === '') { if (oldPass === '') {
//
callback(new Error('旧密码不能为空')) callback(new Error('旧密码不能为空'))
} else { } else {
//
callback() callback()
} }
}, },
//
trigger: 'blur' trigger: 'blur'
} }
], ],
newPass: [ newPass: [
{ {
//
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
//
const newPass = value?.trim() const newPass = value?.trim()
if (newPass === '') { if (newPass === '') {
//
callback(new Error('新密码不能为空')) callback(new Error('新密码不能为空'))
} else { } else {
//
callback() callback()
} }
}, },
//
trigger: 'blur' trigger: 'blur'
} }
], ],
confirmPassword: [ confirmPassword: [
{ {
//
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
//
const confirmPass = value?.trim() const confirmPass = value?.trim()
//
const newPass = formData.value.newPass.trim() const newPass = formData.value.newPass.trim()
if (confirmPass === '') { if (confirmPass === '') {
//
callback(new Error('确认密码不能为空')) callback(new Error('确认密码不能为空'))
} else if (confirmPass !== newPass) { } else if (confirmPass!== newPass) {
//
callback('与新密码不一致') callback('与新密码不一致')
} else { } else {
//
callback() callback()
} }
}, },
//
trigger: 'blur' trigger: 'blur'
} }
] ]
@ -197,9 +258,13 @@ const editPassRules = reactive<FormRules>({
<style lang="scss" scoped> <style lang="scss" scoped>
.navbar-bg-hover { .navbar-bg-hover {
img { img {
//
border-radius: 50%; border-radius: 50%;
//
height: 22px; height: 22px;
//
width: 22px; width: 22px;
//
margin-right: 10px; margin-right: 10px;
} }
} }

@ -1,11 +1,21 @@
<template> <template>
<!-- Element Plus 的面包屑导航组件 el-breadcrumb -->
<!-- 添加了自定义的类名 "breadcrumb" 用于样式设置 -->
<!-- separator="/" 设置面包屑导航的分隔符为 "/" -->
<el-breadcrumb class="breadcrumb" separator="/"> <el-breadcrumb class="breadcrumb" separator="/">
<!-- 过渡组组件 transition-group用于实现面包屑导航项的过渡效果 -->
<!-- 定义了过渡的名称为 "breadcrumb" -->
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<!-- 循环渲染面包屑导航项 el-breadcrumb-item -->
<!-- v-for="item in breadcrumbList" 表示遍历 breadcrumbList 数组中的每一项 -->
<!-- :key="item.path" 为每个面包屑导航项设置唯一的 key -->
<!-- :to="{ path: item.path }" 为面包屑导航项设置跳转的路由路径 -->
<el-breadcrumb-item <el-breadcrumb-item
v-for="item in breadcrumbList" v-for="item in breadcrumbList"
:key="item.path" :key="item.path"
:to="{ path: item.path }" :to="{ path: item.path }"
> >
<!-- 显示面包屑导航项的标题 item.meta.title 中获取如果不存在则显示空 -->
{{ item.meta?.title }} {{ item.meta?.title }}
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
@ -13,32 +23,47 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// Vue watch ref
import { watch, ref } from 'vue' import { watch, ref } from 'vue'
// vue-router useRoute
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
// vue-router RouteRecordRaw
import { RouteRecordRaw } from 'vue-router' import { RouteRecordRaw } from 'vue-router'
//
const route = useRoute() const route = useRoute()
// // breadcrumbList
const breadcrumbList = ref<RouteRecordRaw[]>([]) const breadcrumbList = ref<RouteRecordRaw[]>([])
// initBreadcrumbList
const initBreadcrumbList = () => { const initBreadcrumbList = () => {
// layout // layout layout 使 route.matched.slice(1)
breadcrumbList.value = route.matched.slice(1) breadcrumbList.value = route.matched.slice(1)
} }
// route
watch( watch(
route, route,
() => { () => {
// route initBreadcrumbList
initBreadcrumbList() initBreadcrumbList()
}, },
{ deep: true, immediate: true } {
// deep: true route
// immediate: true
deep: true,
immediate: true
}
) )
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .el-breadcrumb
.el-breadcrumb { .el-breadcrumb {
//
display: flex; display: flex;
// 使
align-items: center; align-items: center;
// 10px
margin-left: 10px; margin-left: 10px;
} }
</style> </style>

@ -1,32 +1,50 @@
<template> <template>
<!-- 定义一个具有类名 "nav-container" 的顶级 div 元素作为导航栏的容器 -->
<div class="nav-container"> <div class="nav-container">
<!-- 导航栏左侧区域类名为 "nav-left" -->
<div class="nav-left"> <div class="nav-left">
<!-- 引入并使用名为 Breadcrumb 的组件该组件可能是用于显示面包屑导航 -->
<Breadcrumb /> <Breadcrumb />
</div> </div>
<!-- 导航栏右侧区域类名为 "nav-right" -->
<div class="nav-right"> <div class="nav-right">
<!-- 引入并使用名为 Avatar 的组件该组件可能是用于显示用户头像等信息 -->
<Avatar class="nav-item" /> <Avatar class="nav-item" />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// components Avatar Vue
import Avatar from './components/Avatar.vue' import Avatar from './components/Avatar.vue'
// components Breadcrumb Vue
import Breadcrumb from './components/Breadcrumb.vue' import Breadcrumb from './components/Breadcrumb.vue'
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .nav-container
.nav-container { .nav-container {
//
display: flex; display: flex;
// 使
justify-content: space-between; justify-content: space-between;
// 使
align-items: center; align-items: center;
// 100%
width: 100%; width: 100%;
// 48
height: 48px; height: 48px;
//
overflow: hidden; overflow: hidden;
//
background-color: #fff; background-color: #fff;
// 1 线 60%
border-bottom: 1px solid #d8d8d860; border-bottom: 1px solid #d8d8d860;
// .nav-right .nav-left
.nav-right, .nav-right,
.nav-left { .nav-left {
//
display: flex; display: flex;
} }
} }

@ -0,0 +1,88 @@
<template>
<!-- 注释说明该部分是列设置 -->
<!-- el-drawer Element Plus 中的抽屉组件用于展示列设置的内容 -->
<!-- title="列设置" 设置抽屉的标题 -->
<!-- v-model="drawerVisible" 双向绑定抽屉的显示隐藏状态drawerVisible 是一个响应式变量 -->
<!-- size="450px" 设置抽屉的宽度为 450px -->
<el-drawer title="列设置" v-model="drawerVisible" size="450px">
<!-- 定义一个包含表格的 div 容器 -->
<div class="table-main">
<!-- el-table Element Plus 中的表格组件用于展示列设置的数据 -->
<!-- :data="colSetting" 绑定表格的数据colSetting 是一个数组包含列的设置信息 -->
<!-- :border="true" 设置表格有边框 -->
<!-- row-key="prop" 为表格的每一行设置唯一的标识这里使用 prop 作为标识 -->
<!-- default-expand-all 表示默认展开所有行对于树形表格 -->
<!-- :tree-props="{ children: '_children' }" 配置表格为树形表格指定子节点的字段为 _children -->
<el-table
:data="colSetting"
:border="true"
row-key="prop"
default-expand-all
:tree-props="{ children: '_children' }"
>
<!-- el-table-column 是表格列组件用于定义表格的列 -->
<!-- prop="label" 绑定列的数据字段为 label -->
<!-- align="center" 设置列内容居中对齐 -->
<!-- label="列名" 设置列的标题为列名 -->
<el-table-column prop="label" align="center" label="列名" />
<!-- 定义显示列prop="isShow" 绑定数据字段为 isShow -->
<!-- v-slot="scope" 定义作用域插槽scope 包含当前行的数据 -->
<el-table-column
prop="isShow"
align="center"
label="显示"
v-slot="scope"
>
<!-- el-switch Element Plus 中的开关组件v-model="scope.row.isShow" 双向绑定当前行的 isShow 字段 -->
<el-switch v-model="scope.row.isShow"></el-switch>
</el-table-column>
<!-- 定义排序列prop="sortable" 绑定数据字段为 sortable -->
<!-- v-slot="scope" 定义作用域插槽scope 包含当前行的数据 -->
<el-table-column
prop="sortable"
align="center"
label="排序"
v-slot="scope"
>
<!-- el-switch Element Plus 中的开关组件v-model="scope.row.sortable" 双向绑定当前行的 sortable 字段 -->
<el-switch v-model="scope.row.sortable"></el-switch>
</el-table-column>
<!-- 定义表格为空时显示的内容#empty 是表格的 empty 插槽 -->
<template #empty>
<div class="table-empty">
<div>暂无可配置列</div>
</div>
</template>
</el-table>
</div>
</el-drawer>
</template>
<script setup lang="ts" name="ColSetting">
// Vue ref
import { ref } from 'vue'
// ProTable ColumnProps
import { ColumnProps } from '@/components/ProTable/interface'
// 使 defineProps colSetting ColumnProps
defineProps<{ colSetting: ColumnProps[] }>()
// drawerVisible false
const drawerVisible = ref<boolean>(false)
// openColSetting drawerVisible true
const openColSetting = () => {
drawerVisible.value = true
}
// openColSetting 便
defineExpose({
openColSetting
})
</script>
<style scoped lang="scss">
// cursor-move move
.cursor-move {
cursor: move;
}
</style>

@ -0,0 +1,47 @@
<template>
<!-- 注释说明该部分是分页组件 -->
<!-- el-pagination Element Plus 中的分页组件 -->
<!-- :current-page="pageable.pageNum" 双向绑定当前页码pageable.pageNum 是一个响应式变量表示当前页码 -->
<!-- :page-size="pageable.pageSize" 双向绑定每页显示的数量pageable.pageSize 是一个响应式变量表示每页显示的数量 -->
<!-- :page-sizes="[10, 25, 50, 100]" 设置每页显示数量的可选值 -->
<!-- :background="true" 设置分页组件的背景颜色 -->
<!-- layout="total, sizes, prev, pager, next, jumper" 设置分页组件的布局包括总数量每页显示数量选择器上一页页码下一页跳转到指定页 -->
<!-- :total="pageable.total" 绑定总数量pageable.total 是一个响应式变量表示数据的总数量 -->
<!-- @size-change="handleSizeChange" 监听每页显示数量改变的事件当用户选择不同的每页显示数量时会调用 handleSizeChange 函数 -->
<!-- @current-change="handleCurrentChange" 监听当前页码改变的事件当用户点击上一页下一页或跳转到指定页时会调用 handleCurrentChange 函数 -->
<el-pagination
:current-page="pageable.pageNum"
:page-size="pageable.pageSize"
:page-sizes="[10, 25, 50, 100]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="pageable.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></el-pagination>
</template>
<script setup lang="ts" name="Pagination">
// Pageable
// pageNum number
// pageSize number
// total number
interface Pageable {
pageNum: number
pageSize: number
total: number
}
// PaginationProps
// pageable Pageable
// handleSizeChange number size
// handleCurrentChange number currentPage
interface PaginationProps {
pageable: Pageable
handleSizeChange: (size: number) => void
handleCurrentChange: (currentPage: number) => void
}
// 使 defineProps PaginationProps
defineProps<PaginationProps>()
</script>

@ -0,0 +1,103 @@
<template>
<!-- 使用动态组件根据 renderLoop 函数的返回值来渲染不同的表格列 -->
<component :is="renderLoop(column)"></component>
</template>
<script lang="tsx" setup name="TableColumn">
// vue
import { inject, ref, useSlots } from 'vue'
// ProTable ColumnProps
import { ColumnProps } from '@/components/ProTable/interface'
//
import { filterEnum, formatValue, handleRowAccordingToProp } from '@/utils/util'
// 使 defineProps column ColumnProps
defineProps<{ column: ColumnProps }>()
//
const slots = useSlots()
// enumMap Ref<Map<any, any>> Map
const enumMap = inject('enumMap', ref(new Map()))
// renderCellData
const renderCellData = (item: ColumnProps, scope: { [key: string]: any }) => {
// prop enumMap
return enumMap.value.get(item.prop) && item.isFilterEnum
// filterEnum
? filterEnum(
handleRowAccordingToProp(scope.row, item.prop!),
enumMap.value.get(item.prop)!,
item.fieldNames
)
//
: formatValue(handleRowAccordingToProp(scope.row, item.prop!))
}
// getTagType
const getTagType = (item: ColumnProps, scope: { [key: string]: any }) => {
// filterEnum
return filterEnum(
handleRowAccordingToProp(scope.row, item.prop!),
enumMap.value.get(item.prop),
item.fieldNames,
'tag'
) as any
}
// renderLoop
const renderLoop = (item: ColumnProps) => {
return (
<>
//
{item.isShow && (
// el-table-column
<el-table-column
//
{...item}
//
align={item.align?? 'center'}
// prop 'operation'
showOverflowTooltip={
item.showOverflowTooltip?? item.prop!== 'operation'
}
>
{{
//
default: (scope: any) => {
//
if (item._children)
//
return item._children.map(child => renderLoop(child))
//
if (item.render) return item.render(scope)
// prop
if (slots[item.prop!]) return slots[item.prop!]!(scope)
//
if (item.tag)
return (
// el-tag
<el-tag type={getTagType(item, scope)}>
{renderCellData(item, scope)}
</el-tag>
)
//
return renderCellData(item, scope)
},
//
header: () => {
//
if (item.headerRender) return item.headerRender(item)
// propHeader
if (slots[`${item.prop}Header`])
return slots[`${item.prop}Header`]!({ row: item })
// label
return item.label
}
}}
</el-table-column>
)}
</>
)
}
</script>

@ -0,0 +1,87 @@
## ProTable 文档 📚
### 1、ProTable 属性ProTableProps
> 使用 `v-bind="$attrs"` 通过属性透传将 **ProTable** 组件属性全部透传到 **el-table** 上,所以我们支持 **el-table** 的所有 **Props** 属性。在此基础上,还扩展了以下 **Props**
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
| :----------: | :---------: | :------: | :-----------------------------------: | :--------------------------------------------------------------------------------------------------: |
| columns | ColumnProps | ✅ | — | ProTable 组件会根据此字段渲染搜索表单与表格列,详情见 ColumnProps |
| requestApi | Function | ✅ | — | 获取表格数据的请求 API |
| requestAuto | Boolean | ❌ | true | 表格初始化是否自动执行请求 API |
| dataCallback | Function | ❌ | — | 后台返回数据的回调函数,可对后台返回数据进行处理 |
| title | String | ❌ | — | 表格标题,目前只在打印的时候用到 |
| pagination | Boolean | ❌ | true | 是否显示分页组件pagination 为 false 后台返回数据应该没有分页信息 和 list 字段data 就是 list 数据 |
| initParam | Object | ❌ | {} | 表格请求的初始化参数,该值变化会自动请求表格数据 |
| toolButton | Boolean | ❌ | true | 是否显示表格功能按钮 |
| rowKey | String | ❌ | 'id' | 当表格数据多选时,所指定的 id |
| searchCol | Object | ❌ | { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 } | 表格搜索项每列占比配置 |
### 2、Column 配置ColumnProps
> 使用 `v-bind="column"` 通过属性透传将每一项 **column** 属性全部透传到 **el-table-column** 上,所以我们支持 **el-table-column** 的所有 **Props** 属性。在此基础上,还扩展了以下 **Props**
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
| :----------: | :----------------: | :------: | :----: | :---------------------------------------------------------------------------------------------: |
| tag | Boolean | ❌ | false | 当前单元格值是否为标签展示,可通过 enum 数据中 tagType 字段指定 tag 类型 |
| isShow | Boolean | ❌ | true | 当前列是否显示在表格内(只对 prop 列生效) |
| search | SearchProps | ❌ | — | 搜索项配置,详情见 SearchProps |
| enum | Object \| Function | ❌ | — | 字典,可格式化单元格内容,还可以作为搜索框的下拉选项(字典可以为 API 请求函数,内部会自动执行) |
| isFilterEnum | Boolean | ❌ | true | 当前单元格值是否根据 enum 格式化(例如 enum 只作为搜索项数据,不参与内容格式化) |
| fieldNames | Object | ❌ | — | 指定字典 label && value 的 key 值 |
| headerRender | Function | ❌ | — | 自定义表头内容渲染tsx 语法、h 语法) |
| render | Function | ❌ | — | 自定义单元格内容渲染tsx 语法、h 语法) |
| \_children | ColumnProps | ❌ | — | 多级表头 |
### 3、搜索项 配置SearchProps
> 使用 `v-bind="column.search.props“` 通过属性透传将 **search.props** 属性全部透传到每一项搜索组件上,所以我们支持 **input、select、tree-select、date-packer、time-picker、time-select、switch** 大部分属性,并在其基础上还扩展了以下 **Props**
| 属性名 | 类型 | 是否必传 | 默认值 | 属性描述 |
| :----------: | :----: | :------: | :----: | :--------------------------------------------------------------------------------------------------------------------------------------------: |
| el | String | ✅ | — | 当前项搜索框的类型支持input、input-number、select、select-v2、tree-select、cascader、date-packer、time-picker、time-select、switch、slider |
| props | Object | ❌ | — | 根据 element plus 官方文档来传递,该属性所有值会透传到组件 |
| defaultValue | Any | ❌ | — | 搜索项默认值 |
| key | String | ❌ | — | 当搜索项 key 不为 prop 属性时,可通过 key 指定 |
| order | Number | ❌ | — | 搜索项排序(从小到大) |
| span | Number | ❌ | 1 | 搜索项所占用的列数,默认为 1 列 |
| offset | Number | ❌ | — | 搜索字段左侧偏移列数 |
### 4、ProTable 事件:
> 根据 **ElementPlus Table** 文档在 **ProTable** 组件上绑定事件即可,组件会通过 **$attrs** 透传给 **el-table**
>
> [el-table 事件文档链接](https://element-plus.org/zh-CN/component/table.html#table-%E4%BA%8B%E4%BB%B6)
### 5、ProTable 方法:
> **ProTable** 组件暴露了 **el-table** 实例和一些组件内部的参数和方法:
>
> [el-table 方法文档链接](https://element-plus.org/zh-CN/component/table.html#table-%E6%96%B9%E6%B3%95)
| 方法名 | 描述 |
| :-------------: | :-------------------------------------------------------------------: |
| element | `el-table` 实例,可以通过`element.方法名`来调用 `el-table` 的所有方法 |
| tableData | 当前页面所展示的数据 |
| searchParam | 所有的搜索参数,不包含分页 |
| pageable | 当前表格的分页数据 |
| getTableList | 获取、刷新表格数据的方法(携带所有参数) |
| reset | 重置表格查询参数,相当于点击重置搜索按钮 |
| clearSelection | 清空表格所选择的数据,除此方法之外还可使用 `element.clearSelection()` |
| enumMap | 当前表格使用的所有字典数据Map 数据结构) |
| isSelected | 表格是否选中数据 |
| selectedList | 表格选中的数据列表 |
| selectedListIds | 表格选中的数据列表的 id |
### 6、ProTable 插槽:
| 插槽名 | 描述 |
| :----------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: |
| — | 默认插槽,支持直接在 ProTable 中写 el-table-column 标签 |
| tableHeader | 自定义表格头部左侧区域的插槽,一般情况该区域放操作按钮 |
| toolButton | 自定义表格头部左右侧侧功能区域的插槽 |
| append | 插入至表格最后一行之后的内容, 如果需要对表格的内容进行无限滚动操作,可能需要用到这个 slot。 若表格有合计行,该 slot 会位于合计行之上。 |
| empty | 当表格数据为空时自定义的内容 |
| pagination | 分页组件插槽 |
| `column.prop` | 单元格的作用域插槽 |
| `column.prop` + "Header" | 表头的作用域插槽 |

@ -0,0 +1,397 @@
<!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7166068828202336263 -->
<template>
<!-- 查询表单 card -->
<!-- SearchForm 是自定义的搜索表单组件 -->
<!-- :search="search" 绑定搜索方法 -->
<!-- :reset="reset" 绑定重置方法 -->
<!-- :searchParam="searchParam" 绑定搜索参数 -->
<!-- :columns="searchColumns" 绑定搜索列配置 -->
<!-- :searchCol="searchCol" 绑定搜索项每列占比配置 -->
<!-- v-show="isShowSearch" 根据 isShowSearch 的值显示或隐藏搜索表单 -->
<!-- ref="searchForm" 为搜索表单组件添加引用 -->
<SearchForm
:search="search"
:reset="reset"
:searchParam="searchParam"
:columns="searchColumns"
:searchCol="searchCol"
v-show="isShowSearch"
ref="searchForm"
/>
<!-- 表格内容 card -->
<MyCard class="mt-2">
<div class="card table-main">
<div class="table-header">
<div class="flex justify-between header-button-lf mb-2">
<div>
<!-- 具名插槽 tableHeader传递了 selectedListIdsselectedList isSelected 数据 -->
<slot
name="tableHeader"
:selectedListIds="selectedListIds"
:selectedList="selectedList"
:isSelected="isSelected"
/>
</div>
<div class="header-button-ri">
<!-- 具名插槽 toolButton包含了一些功能按钮 -->
<slot name="toolButton">
<!-- 刷新按钮点击时调用 getTableList 方法获取表格数据 -->
<el-button :icon="Refresh" circle @click="getTableList" />
<!-- <el-button :icon="Printer" circle v-if="columns.length" @click="handlePrint" /> -->
<!-- 列设置按钮 columns 存在时显示点击时调用 openColSetting 方法打开列设置 -->
<el-button
:icon="Operation"
circle
v-if="columns.length"
@click="openColSetting"
/>
<!-- 搜索按钮 searchColumns 存在时显示点击时切换 isShowSearch 的值显示或隐藏搜索表单 -->
<el-button
:icon="Search"
circle
v-if="searchColumns.length"
@click="isShowSearch = !isShowSearch"
/>
</slot>
</div>
</div>
</div>
<!-- 表格主体 -->
<!-- el-table Element Plus 的表格组件 -->
<!-- ref="tableRef" 为表格组件添加引用 -->
<!-- v-bind="$attrs" 绑定父组件传递的所有属性 -->
<!-- :data="tableData" 绑定表格数据 -->
<!-- :border="border" 绑定表格是否有纵向边框 -->
<!-- :row-key="rowKey" 绑定行数据的唯一标识 -->
<!-- @selection-change="selectionChange" 监听表格行选择变化事件 -->
<el-table
ref="tableRef"
v-bind="$attrs"
:data="tableData"
:border="border"
:row-key="rowKey"
@selection-change="selectionChange"
>
<!-- 默认插槽 -->
<slot></slot>
<!-- 循环渲染表格列 -->
<template v-for="item in tableColumns" :key="item">
<!-- selection || index 类型的列 -->
<el-table-column
v-bind="item"
:align="item.align?? 'center'"
:reserve-selection="item.type =='selection'"
v-if="item.type =='selection' || item.type == 'index'"
>
</el-table-column>
<!-- expand 类型的列支持 tsx 语法和作用域插槽 -->
<el-table-column
v-bind="item"
:align="item.align?? 'center'"
v-if="item.type == 'expand'"
v-slot="scope"
>
<!-- 如果有自定义渲染函数则使用该函数渲染 -->
<component :is="item.render" :row="scope.row" v-if="item.render">
</component>
<!-- 否则使用插槽渲染 -->
<slot :name="item.type" :row="scope.row" v-else></slot>
</el-table-column>
<!-- 其他类型的列通过 TableColumn 组件递归渲染 -->
<TableColumn
v-if="!item.type && item.prop && item.isShow"
:column="item"
>
<!-- 循环渲染插槽内容 -->
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
<slot :name="slot" :row="scope.row"></slot>
</template>
</TableColumn>
</template>
<!-- 插入表格最后一行之后的插槽 -->
<template #append>
<slot name="append"> </slot>
</template>
<!-- 表格无数据情况 -->
<template #empty>
<div class="table-empty">
<slot name="empty">
<div>暂无数据</div>
</slot>
</div>
</template>
</el-table>
<!-- 分页组件插槽 -->
<slot name="pagination">
<div class="mt-2 flex flex-row-reverse">
<!-- Pagination 是自定义的分页组件 pagination true 时显示 -->
<Pagination
v-if="pagination"
:pageable="pageable"
:handleSizeChange="handleSizeChange"
:handleCurrentChange="handleCurrentChange"
/>
</div>
</slot>
</div>
</MyCard>
<!-- 列设置 -->
<!-- ColSetting 是自定义的列设置组件 toolButton true 时显示 -->
<!-- v-model:colSetting="colSetting" 双向绑定列设置数据 -->
<ColSetting v-if="toolButton" ref="colRef" v-model:colSetting="colSetting" />
</template>
<script setup lang="ts" name="ProTable">
// Vue
import { ref, watch, computed, provide, onMounted } from 'vue'
// Hooks
import { useTable } from '@/hooks/useTable'
//
import { BreakPoint } from '@/components/Grid/interface'
//
import { ColumnProps } from '@/components/ProTable/interface'
// Element Plus
import { ElTable, TableProps } from 'element-plus'
// Element Plus
import { Refresh, Printer, Operation, Search } from '@element-plus/icons-vue'
//
import {
filterEnum,
formatValue,
handleProp,
handleRowAccordingToProp
} from '@/utils/util'
//
import SearchForm from '@/components/SearchForm/index.vue'
//
import Pagination from './components/Pagination.vue'
//
import ColSetting from './components/ColSetting.vue'
//
import TableColumn from './components/TableColumn.vue'
// Hooks
import { useSelection } from '@/hooks/useSelection'
// import printJS from "print-js";
//
const searchForm = ref()
// ProTable TableProps data
interface ProTableProps extends Partial<Omit<TableProps<any>, 'data'>> {
//
columns: ColumnProps[]
// API
requestApi: (params: any) => Promise<any>
//
requestAuto?: boolean
//
dataCallback?: (data: any) => any
//
title?: string
// true
pagination?: boolean
// {}
initParam?: any
// true
border?: boolean
// true
toolButton?: boolean
// Key Table id id
rowKey?: string
// { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
searchCol?: number | Record<BreakPoint, number>
}
//
const props = withDefaults(defineProps<ProTableProps>(), {
requestAuto: true,
columns: () => [],
pagination: true,
initParam: {},
border: true,
toolButton: true,
rowKey: 'id',
searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 })
})
//
const isShowSearch = ref(true)
// DOM
const tableRef = ref<InstanceType<typeof ElTable>>()
// 使 Hooks
const { selectionChange, selectedList, selectedListIds, isSelected } =
useSelection(props.rowKey)
// 使 Hooks
const {
tableData,
pageable,
searchParam,
searchInitParam,
getTableList,
search,
reset,
handleSizeChange,
handleCurrentChange
} = useTable(
props.requestApi,
props.initParam,
props.pagination,
props.dataCallback
)
//
const clearSelection = () => tableRef.value!.clearSelection()
// requestAuto true getTableList
onMounted(() => props.requestAuto && getTableList())
// initParam
watch(() => props.initParam, getTableList, { deep: true })
//
const tableColumns = ref<ColumnProps[]>(props.columns)
// enumMap provide
const enumMap = ref(new Map<string, { [key: string]: any }[]>())
provide('enumMap', enumMap)
// enumMap
const setEnumMap = async (col: ColumnProps) => {
if (!col.enum) return
// enum enumMap
if (typeof col.enum!== 'function')
return enumMap.value.set(col.prop!, col.enum!)
// enum enumMap
const { data } = await col.enum()
enumMap.value.set(col.prop!, data)
}
// columns
const flatColumnsFunc = (
columns: ColumnProps[],
flatArr: ColumnProps[] = []
) => {
columns.forEach(async col => {
//
if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children))
flatArr.push(col)
// column isShow isFilterEnum
col.isShow = col.isShow?? true
col.isFilterEnum = col.isFilterEnum?? true
// enumMap
setEnumMap(col)
})
//
return flatArr.filter(item =>!item._children?.length)
}
// columns
const flatColumns = ref<ColumnProps[]>()
flatColumns.value = flatColumnsFunc(tableColumns.value)
//
const searchColumns = flatColumns.value.filter(item => item.search?.el)
//
searchColumns.forEach((column, index) => {
column.search!.order = column.search!.order?? index + 2
if (
column.search?.defaultValue!== undefined &&
column.search?.defaultValue!== null
) {
searchInitParam.value[column.search.key?? handleProp(column.prop!)] =
column.search?.defaultValue
searchParam.value[column.search.key?? handleProp(column.prop!)] =
column.search?.defaultValue
}
})
//
searchColumns.sort((a, b) => a.search!.order! - b.search!.order!)
//
const colRef = ref()
//
const colSetting = tableColumns.value!.filter(
item =>
![
'selection',
'index',
'expand'
].includes(item.type!) && item.prop!== 'operation'
)
//
const openColSetting = () => colRef.value.openColSetting()
// 201-238
// enum
const printData = computed(() => {
const printDataList = JSON.parse(
JSON.stringify(
selectedList.value.length? selectedList.value : tableData.value
)
)
//
const needTransformCol = flatColumns.value!.filter(
item =>
(item.enum || (item.prop && item.prop.split('.').length > 1)) &&
item.isFilterEnum
)
needTransformCol.forEach(colItem => {
printDataList.forEach((tableItem: { [key: string]: any }) => {
tableItem[handleProp(colItem.prop!)] =
colItem.prop!.split('.').length > 1 &&!colItem.enum
? formatValue(handleRowAccordingToProp(tableItem, colItem.prop!))
: filterEnum(
handleRowAccordingToProp(tableItem, colItem.prop!),
enumMap.value.get(colItem.prop!),
colItem.fieldNames
)
for (const key in tableItem) {
if (tableItem[key] === null)
tableItem[key] = formatValue(tableItem[key])
}
})
})
return printDataList
})
// 💥 printJs
// const handlePrint = () => {
// const header = `<div style="text-align: center"><h2>${props.title}</h2></div>`;
// const gridHeaderStyle = "border: 1px solid #ebeef5;height: 45px;color: #232425;text-align: center;background-color: #fafafa;";
// const gridStyle = "border: 1px solid #ebeef5;height: 40px;color: #494b4e;text-align: center";
// printJS({
// printable: printData.value,
// header: props.title && header,
// properties: flatColumns
// .value!.filter(item => !["selection", "index", "expand"].includes(item.type!) && item.isShow && item.prop !== "operation")
// .map((item: ColumnProps) => ({ field: handleProp(item.prop!), displayName: item.label })),
// type: "json",
// gridHeaderStyle,
// gridStyle
// });
// };
//
defineExpose({
element: tableRef,
tableData,
searchParam,
pageable,
getTableList,
reset,
clearSelection,
enumMap,
isSelected,
selectedList,
selectedListIds
})
</script>

@ -0,0 +1,84 @@
// 从 "element-plus/es/components/table/src/table-column/defaults" 导入 TableColumnCtx 类型
// TableColumnCtx 可能是 Element Plus 中表格列的上下文类型,包含了表格列的一些默认属性和方法
import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
// 从 "@/components/Grid/interface" 导入 BreakPoint 和 Responsive 类型
// BreakPoint 可能表示响应式布局中的不同断点(如 xs, sm, md, lg, xl 等)
// Responsive 可能用于描述在不同断点下的响应式配置
import { BreakPoint, Responsive } from "@/components/Grid/interface";
// 定义一个接口 EnumProps用于描述枚举属性
export interface EnumProps {
// 选项框显示的文字,类型为字符串
label: string;
// 选项框值,可以是任意类型
value: any;
// 是否禁用此选项,可选,默认为 undefined类型为布尔值
disabled?: boolean;
// 当 tag 为 true 时,此选择会指定 tag 显示类型,可选,默认为 undefined类型为字符串
tagType?: string;
// 为树形选择时,可以通过 children 属性指定子选项,可选,默认为 undefined类型为 EnumProps 数组
children?: EnumProps[];
// 允许添加任意其他属性,属性名和值的类型都为任意类型
[key: string]: any;
}
// 定义一个类型别名 TypeProp其值只能是 "index"、"selection" 或 "expand" 中的一个
export type TypeProp = "index" | "selection" | "expand";
// 定义一个类型别名 SearchType用于表示搜索框的类型
// 可以是 "input"、"input-number" 等多种 Element Plus 组件类型
export type SearchType =
| "input"
| "input-number"
| "select"
| "select-v2"
| "tree-select"
| "cascader"
| "date-picker"
| "time-picker"
| "time-select"
| "switch"
| "slider";
// 定义一个接口 SearchProps用于描述搜索项的属性
export type SearchProps = {
// 当前项搜索框的类型,类型为 SearchType
el: SearchType;
// 搜索项参数,根据 element plus 官方文档来传递,该属性所有值会透传到组件,可选,默认为 undefined
props?: any;
// 当搜索项 key 不为 prop 属性时,可通过 key 指定,可选,默认为 undefined类型为字符串
key?: string;
// 搜索项排序(从大到小),可选,默认为 undefined类型为数字
order?: number;
// 搜索项所占用的列数,默认为 1 列,可选,默认为 undefined类型为数字
span?: number;
// 搜索字段左侧偏移列数,可选,默认为 undefined类型为数字
offset?: number;
// 搜索项默认值,可以是字符串、数字、布尔值或任意类型的数组,可选,默认为 undefined
defaultValue?: string | number | boolean | any[];
// 扩展为包含 BreakPoint 类型的键和 Responsive 类型的值的部分记录,用于实现响应式配置
} & Partial<Record<BreakPoint, Responsive>>;
// 定义一个接口 ColumnProps用于描述表格列的属性
// 扩展自 Partial<Omit<TableColumnCtx<T>, "children" | "renderHeader" | "renderCell">>,表示部分继承 TableColumnCtx 类型并去除 "children"、"renderHeader" 和 "renderCell" 属性
export interface ColumnProps<T = any>
extends Partial<Omit<TableColumnCtx<T>, "children" | "renderHeader" | "renderCell">> {
// 是否是标签展示,可选,默认为 undefined类型为布尔值
tag?: boolean;
// 是否显示在表格当中,可选,默认为 undefined类型为布尔值
isShow?: boolean;
// 搜索项配置,可选,默认为 undefined类型为 SearchProps 或 undefined
search?: SearchProps | undefined;
// 枚举类型(渲染值的字典),可以是 EnumProps 数组或一个返回 Promise<any> 的函数,可选,默认为 undefined
enum?: EnumProps[] | ((params?: any) => Promise<any>);
// 当前单元格值是否根据 enum 格式化示例enum 只作为搜索项数据),可选,默认为 undefined类型为布尔值
isFilterEnum?: boolean;
// 指定 label && value 的 key 值,可选,默认为 undefined类型为包含 label 和 value 键的对象
fieldNames?: { label: string; value: string };
// 自定义表头内容渲染tsx语法可选默认为 undefined类型为一个接受 ColumnProps 类型参数并返回任意类型的函数
headerRender?: (row: ColumnProps) => any;
// 自定义单元格内容渲染tsx语法可选默认为 undefined类型为一个接受包含 row 属性且类型为 T 的对象并返回任意类型的函数
render?: (scope: { row: T }) => any;
// 多级表头,可选,默认为 undefined类型为 ColumnProps<T> 数组
_children?: ColumnProps<T>[];
}

@ -0,0 +1,12 @@
// 从当前目录下的 src 文件夹中的 index.vue 文件导入组件
// 这里将导入的组件命名为 reImageVerify它应该是一个 Vue 组件
import reImageVerify from './src/index.vue'
/** 图形验证码组件 */
// 使用具名导出的方式,将导入的组件以 ReImageVerify 这个名称导出
// 具名导出允许在导入时指定具体要导入的内容,方便在其他文件中使用
export const ReImageVerify = reImageVerify
// 使用默认导出的方式,将导入的组件作为默认导出
// 默认导出允许在导入时可以使用任意名称来接收该组件,通常一个文件只能有一个默认导出
export default ReImageVerify

@ -0,0 +1,133 @@
import { ref, onMounted } from 'vue'
/**
*
* @param width -
* @param height -
*/
// 定义一个名为 useImageVerify 的函数,用于生成和管理图形验证码,接受图形宽度和高度作为参数,有默认值
export const useImageVerify = (width = 120, height = 40) => {
// 创建一个响应式引用 domRef用于存储 <canvas> 元素的引用
const domRef = ref<HTMLCanvasElement>()
// 创建一个响应式变量 imgCode用于存储生成的图形验证码字符串
const imgCode = ref('')
// 定义一个函数 setImgCode用于设置 imgCode 的值
function setImgCode(code: string) {
imgCode.value = code
}
// 定义一个函数 getImgCode用于在 <canvas> 上绘制验证码并获取生成的验证码字符串
function getImgCode() {
// 如果 domRef 没有值(即 <canvas> 元素未挂载),则直接返回
if (!domRef.value) return
// 调用 draw 函数绘制验证码,并将返回的验证码字符串赋值给 imgCode
imgCode.value = draw(domRef.value, width, height)
}
// 在组件挂载后执行的钩子函数,调用 getImgCode 函数生成验证码
onMounted(() => {
getImgCode()
})
// 返回一个包含 domRef、imgCode、setImgCode 和 getImgCode 的对象,以便在其他地方使用这些变量和函数
return {
domRef,
imgCode,
setImgCode,
getImgCode
}
}
// 定义一个函数 randomNum用于生成指定范围内的随机整数
function randomNum(min: number, max: number) {
// 使用 Math.random() 生成一个 0 到 1 之间的随机小数,然后通过计算得到指定范围内的整数
const num = Math.floor(Math.random() * (max - min) + min)
return num
}
// 定义一个函数 randomColor用于生成指定范围内的随机颜色RGB 格式)
function randomColor(min: number, max: number) {
// 分别生成随机的红r、绿g、蓝b分量值
const r = randomNum(min, max)
const g = randomNum(min, max)
const b = randomNum(min, max)
// 返回 RGB 格式的颜色字符串
return `rgb(${r},${g},${b})`
}
// 定义一个函数 draw用于在 <canvas> 元素上绘制图形验证码
function draw(dom: HTMLCanvasElement, width: number, height: number) {
// 初始化一个空字符串 imgCode用于存储生成的验证码
let imgCode = ''
// 定义一个字符串 NUMBER_STRING包含数字 0 到 9用于生成验证码字符
const NUMBER_STRING = '0123456789'
// 获取 <canvas> 元素的 2D 绘图上下文 ctx
const ctx = dom.getContext('2d')
// 如果获取上下文失败ctx 为 null则直接返回空的验证码字符串
if (!ctx) return imgCode
// 设置绘图上下文的填充样式为随机颜色(背景色)
ctx.fillStyle = randomColor(180, 230)
// 使用填充样式绘制一个矩形,覆盖整个 <canvas> 区域,作为验证码的背景
ctx.fillRect(0, 0, width, height)
// 循环 4 次,生成 4 个验证码字符
for (let i = 0; i < 4; i += 1) {
// 从 NUMBER_STRING 中随机选取一个字符作为验证码字符
const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)]
// 将选取的字符添加到 imgCode 字符串中
imgCode += text
// 随机生成字体大小
const fontSize = randomNum(18, 41)
// 随机生成字符的旋转角度
const deg = randomNum(-30, 30)
// 设置绘图上下文的字体样式
ctx.font = `${fontSize}px Simhei`
// 设置文本基线为顶部
ctx.textBaseline = 'top'
// 设置绘图上下文的填充样式为随机颜色(字符颜色)
ctx.fillStyle = randomColor(80, 150)
// 保存当前绘图上下文的状态
ctx.save()
// 平移绘图上下文的原点到指定位置(每个字符的起始位置)
ctx.translate(30 * i + 15, 15)
// 旋转绘图上下文指定的角度
ctx.rotate((deg * Math.PI) / 180)
// 在指定位置绘制字符
ctx.fillText(text, -15 + 5, -15)
// 恢复之前保存的绘图上下文状态
ctx.restore()
}
// 循环 5 次,绘制 5 条随机直线
for (let i = 0; i < 5; i += 1) {
// 开始绘制路径
ctx.beginPath()
// 设置路径的起始点为随机位置
ctx.moveTo(randomNum(0, width), randomNum(0, height))
// 设置路径的终点为随机位置
ctx.lineTo(randomNum(0, width), randomNum(0, height))
// 设置绘图上下文的描边样式为随机颜色
ctx.strokeStyle = randomColor(180, 230)
// 关闭路径
ctx.closePath()
// 绘制路径(直线)
ctx.stroke()
}
// 循环 41 次,绘制 41 个随机圆形
for (let i = 0; i < 41; i += 1) {
// 开始绘制路径
ctx.beginPath()
// 绘制一个圆形,圆心为随机位置,半径为 1
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI)
// 关闭路径
ctx.closePath()
// 设置绘图上下文的填充样式为随机颜色
ctx.fillStyle = randomColor(150, 200)
// 填充圆形
ctx.fill()
}
// 返回生成的验证码字符串
return imgCode
}

@ -0,0 +1,55 @@
<script setup lang="ts">
// Vue watch
import { watch } from 'vue'
// useImageVerify
import { useImageVerify } from './hooks'
// Props
interface Props {
code?: string // code
}
// Emits
interface Emits {
(e: 'update:code', code: string): void // 'update:code' code
}
// 使 withDefaults props
const props = withDefaults(defineProps<Props>(), {
code: '' // code
})
// emit
const emit = defineEmits<Emits>()
// useImageVerify domRefimgCodesetImgCode getImgCode
const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify()
// props.code props.code setImgCode
watch(
() => props.code,
newValue => {
setImgCode(newValue)
}
)
// imgCode imgCode 'update:code'
watch(imgCode, newValue => {
emit('update:code', newValue)
})
// getImgCode 便
defineExpose({ getImgCode })
</script>
<template>
<!-- 定义一个 canvas 元素设置其宽度为 120高度为 40添加类名 cursor-pointer 使其具有指针样式
绑定 ref domRef以便在脚本中访问该元素绑定 click 事件为 getImgCode 函数 -->
<canvas
ref="domRef"
width="120"
height="40"
class="cursor-pointer"
@click="getImgCode"
/>
</template>

@ -0,0 +1,117 @@
<template>
<!-- 根据条件渲染不同的组件 -->
<component
v-if="column.search?.el" <!-- 如果 column.search.el 存在则渲染该组件 -->
:is="`el-${column.search.el}`" <!-- 根据 column.search.el 的值动态渲染对应的 Element Plus 组件例如 'el-input''el-select' -->
v-bind="handleSearchProps" <!-- 绑定处理后的搜索属性 -->
v-model.trim="searchParam[column.search.key?? handleProp(column.prop!)]" <!-- 双向绑定搜索参数使用 trim 修饰符去除首尾空格 -->
:data="column.search?.el === 'tree-select'? columnEnum : []" <!-- 如果是 tree-select 组件绑定 columnEnum 数据否则绑定空数组 -->
:options="
['cascader','select-v2'].includes(column.search?.el)? columnEnum : []
" <!-- 如果是 cascader select-v2 组件绑定 columnEnum 数据作为选项否则绑定空数组 -->
:placeholder="placeholder" <!-- 绑定占位符文本 -->
:clearable="clearable" <!-- 绑定是否可清除的属性 -->
range-separator="至" <!-- 日期范围选择器的分隔符 -->
start-placeholder="开始时间" <!-- 日期范围选择器开始时间的占位符 -->
end-placeholder="结束时间" <!-- 日期范围选择器结束时间的占位符 -->
>
<!-- column.search.el 'cascader' 渲染默认插槽内容 -->
<template #default="{ data }" v-if="column.search.el === 'cascader'">
<span>{{ data[fieldNames.label] }}</span> <!-- 显示数据的 label 字段 -->
</template>
<!-- column.search.el 'select' 渲染子选项 -->
<template v-if="column.search.el ==='select'">
<component
:is="`el-option`" <!-- 渲染 Element Plus el-option 组件 -->
v-for="(col, index) in columnEnum" <!-- 遍历 columnEnum 数据 -->
:key="index" <!-- 设置唯一的 key -->
:label="col[fieldNames.label]" <!-- 绑定选项的 label -->
:value="col[fieldNames.value]" <!-- 绑定选项的值 -->
></component>
</template>
<!-- 如果以上条件都不满足渲染插槽内容 -->
<slot v-else></slot>
</component>
</template>
<script setup lang="ts" name="SearchFormItem">
// Vue
import { computed, inject, onMounted, ref } from 'vue'
// handleProp
import { handleProp } from '@/utils/util'
//
import { ColumnProps } from '@/components/ProTable/interface'
// SearchFormItem
interface SearchFormItem {
column: ColumnProps //
searchParam: { [key: string]: any } //
}
// props
const props = defineProps<SearchFormItem>()
// fieldNames label && value key
const fieldNames = computed(() => {
return {
label: props.column.fieldNames?.label?? 'label', // column.fieldNames.label 使使 'label'
value: props.column.fieldNames?.value?? 'value' // column.fieldNames.value 使使 'value'
}
})
// enumMap
const enumMap = inject('enumMap', ref(new Map()))
// columnEnum props.column.prop enumMap
const columnEnum = computed(() => {
let enumData = enumMap.value.get(props.column.prop)
if (!enumData) return [] //
if (props.column.search?.el ==='select-v2' && props.column.fieldNames) {
enumData = enumData.map((item: { [key: string]: any }) => {
return {
...item,
label: item[fieldNames.value.label], // label
value: item[fieldNames.value.value] // value
}
})
}
return enumData
})
// searchProps(el tree-selectcascader label value)
const handleSearchProps = computed(() => {
const label = fieldNames.value.label
const value = fieldNames.value.value
const searchEl = props.column.search?.el
const searchProps = props.column.search?.props?? {}
let handleProps = searchProps
if (searchEl === 'tree-select')
handleProps = {
...searchProps,
props: { label,...searchProps.props }, // tree-select props
nodeKey: value
}
if (searchEl === 'cascader')
handleProps = {
...searchProps,
props: { label, value,...searchProps.props } // cascader props
}
return handleProps
})
// placeholder
const placeholder = computed(() => {
const search = props.column.search
return (
search?.props?.placeholder?? (search?.el === 'input'? '请输入' : '请选择') //
)
})
// ()
const clearable = computed(() => {
const search = props.column.search
return (
search?.props?.clearable??
(search?.defaultValue == null || search?.defaultValue == undefined) // props
)
})
</script>

@ -0,0 +1,142 @@
<template>
<!-- MyCard 组件可能是一个自定义的卡片组件 -->
<MyCard>
<!-- columns 数组长度大于 0 时显示搜索表单 -->
<div class="card table-search" v-if="columns.length">
<!-- el-form 组件用于创建表单 -->
<el-form ref="formRef" :model="searchParam">
<!-- Grid 组件可能是一个用于布局的网格组件 -->
<Grid
ref="gridRef"
:collapsed="collapsed"
:gap="[20, 0]"
:cols="searchCol"
>
<!-- 循环渲染 GridItem 组件每个 GridItem 对应一个搜索表单字段 -->
<GridItem
v-for="(item, index) in columns"
:key="item.prop"
v-bind="getResponsive(item)"
:index="index"
>
<!-- el-form-item 组件用于创建表单字段 -->
<el-form-item :label="`${item.label} :`">
<!-- SearchFormItem 组件用于渲染具体的搜索表单字段 -->
<SearchFormItem :column="item" :searchParam="searchParam" />
</el-form-item>
</GridItem>
<!-- 带有 suffix 标志的 GridItem 组件用于放置操作按钮 -->
<GridItem suffix>
<div class="operation">
<!-- 搜索按钮点击时调用 search 方法 -->
<el-button
class="bg-blue clickSearchBtn"
type="primary"
:icon="Search"
@click="search"
>
搜索
</el-button>
<!-- 重置按钮点击时调用 reset 方法 -->
<el-button :icon="Delete" @click="reset"></el-button>
<!-- 展开/合并按钮根据 collapsed 的值显示不同的文本和图标 -->
<el-button
v-if="showCollapse"
link
class="search-isOpen"
@click="collapsed =!collapsed"
>
{{ collapsed? '展开' : '合并' }}
<el-icon class="el-icon--right">
<component :is="collapsed? ArrowDown : ArrowUp"></component>
</el-icon>
</el-button>
</div>
</GridItem>
</Grid>
</el-form>
</div>
</MyCard>
</template>
<script setup lang="ts" name="SearchForm">
// Vue
import { computed, onMounted, ref } from 'vue'
// ProTable
import { ColumnProps } from '@/components/ProTable/interface'
// Grid
import { BreakPoint } from '@/components/Grid/interface'
// Element Plus
import { Delete, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue'
// SearchFormItem
import SearchFormItem from './components/SearchFormItem.vue'
// Grid
import Grid from '@/components/Grid/index.vue'
// GridItem
import GridItem from '@/components/Grid/components/GridItem.vue'
// MyCard
import MyCard from '../my-card/my-card.vue'
// ProTableProps
interface ProTableProps {
//
columns?: ColumnProps[]
//
searchParam?: { [key: string]: any }
//
searchCol: number | Record<BreakPoint, number>
//
search: (params: any) => void
//
reset: (params: any) => void
}
// 使 withDefaults
const props = withDefaults(defineProps<ProTableProps>(), {
columns: () => [],
searchParam: () => ({})
})
//
const getResponsive = (item: ColumnProps) => {
return {
// item.search.span
span: item.search?.span,
// item.search.offset 0
offset: item.search?.offset?? 0,
// item.search
xs: item.search?.xs,
sm: item.search?.sm,
md: item.search?.md,
lg: item.search?.lg,
xl: item.search?.xl
}
}
//
const collapsed = ref(true)
// ref Grid
const gridRef = ref()
//
const breakPoint = computed<BreakPoint>(() => gridRef.value?.breakPoint)
// /
const showCollapse = computed(() => {
let show = false
// columns
props.columns.reduce((prev, current) => {
prev +=
(current.search![breakPoint.value]?.span?? current.search?.span?? 1) +
(current.search![breakPoint.value]?.offset?? current.search?.offset?? 0)
// searchCol /
if (typeof props.searchCol!== 'number') {
if (prev >= props.searchCol[breakPoint.value]) show = true
} else {
if (prev > props.searchCol) show = true
}
return prev
}, 0)
return show
})
</script>

@ -1,6 +1,15 @@
<template> <template>
<!-- 定义一个具有类名 "container" div 元素 -->
<div class="container"> <div class="container">
<!-- Element Plus 的工具提示组件 el-tooltip -->
<!-- effect="light" 设置工具提示的效果为淡色 -->
<!-- :content="content" 绑定工具提示的内容内容通过计算属性 content 获取 -->
<!-- placement="right" 设置工具提示显示在目标元素的右侧 -->
<el-tooltip effect="light" :content="content" placement="right"> <el-tooltip effect="light" :content="content" placement="right">
<!-- 自定义的 SVG 图标组件 svg-icon -->
<!-- icon="menu-fold" 设置图标为 "menu-fold" -->
<!-- @click="changeSiderType" 绑定点击事件点击时调用 changeSiderType 方法 -->
<!-- :style="style" 绑定样式样式通过计算属性 style 获取 -->
<svg-icon <svg-icon
icon="menu-fold" icon="menu-fold"
@click="changeSiderType" @click="changeSiderType"
@ -11,31 +20,43 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// '@/store' useStore 访 Vuex
import { useStore } from '@/store' import { useStore } from '@/store'
// 'vue' computed
import { computed } from 'vue' import { computed } from 'vue'
// Vuex
const store = useStore() const store = useStore()
// content store.state.app.siderType
const content = computed(() => { const content = computed(() => {
return store.state.app.siderType ? '点击折叠' : '点击展开' return store.state.app.siderType? '点击折叠' : '点击展开'
}) })
// style store.state.app.siderType
const style = computed(() => { const style = computed(() => {
return store.state.app.siderType ? 'none' : 'transform: rotateY(180deg);' return store.state.app.siderType? 'none' : 'transform: rotateY(180deg);'
}) })
// changeSiderType
const changeSiderType = () => { const changeSiderType = () => {
// 'app/setSiderType' mutation Vuex
store.commit('app/setSiderType') store.commit('app/setSiderType')
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .svg-icon svg-icon
.svg-icon { .svg-icon {
// Element Plus
color: var(--el-color-primary); color: var(--el-color-primary);
margin: 0 0 4px 16px !important; //
vertical-align: middle; vertical-align: middle;
//
outline: none; outline: none;
// 0.36
transition-duration: 0.36s; transition-duration: 0.36s;
//
cursor: pointer; cursor: pointer;
} }
</style> </style>

@ -1,15 +1,28 @@
<template> <template>
<!-- 使用 v-for 循环遍历 menuList 数组为每个菜单项生成对应的 DOM 结构 -->
<!-- :key="menu.id" 为每个循环项设置唯一的 key用于 Vue 的虚拟 DOM 优化 -->
<template v-for="menu in menuList" :key="menu.id"> <template v-for="menu in menuList" :key="menu.id">
<!-- Element Plus 的子菜单组件 el-sub-menu -->
<!-- v-if="menu.children && menu.children.length > 0" 只有当菜单有子菜单时才渲染该子菜单 -->
<!-- :index="menu.name + ''" 设置子菜单的索引 menu.name 转换为字符串类型 -->
<el-sub-menu <el-sub-menu
v-if="menu.children && menu.children.length > 0" v-if="menu.children && menu.children.length > 0"
:index="menu.name + ''" :index="menu.name + ''"
> >
<!-- 子菜单的标题模板 #title -->
<template #title> <template #title>
<!-- 如果菜单有 meta.icon 属性渲染自定义的 SVG 图标组件 svg-icon -->
<svg-icon <svg-icon
v-if="menu.meta.icon" v-if="menu.meta.icon"
:icon="menu.meta.icon" :icon="menu.meta.icon"
class="sub-menu-icon" class="sub-menu-icon"
></svg-icon> ></svg-icon>
<!-- Element Plus 的工具提示组件 el-tooltip -->
<!-- effect="light" 设置工具提示的效果为淡色 -->
<!-- :content="menu.meta.title" 绑定工具提示的内容为菜单的标题 -->
<!-- placement="top-start" 设置工具提示显示在目标元素的顶部偏左位置 -->
<!-- :offset="-10" 设置工具提示与目标元素的偏移量为 -10 像素 -->
<!-- :disabled="!showTooltip" 根据 showTooltip 的值决定是否禁用工具提示 -->
<el-tooltip <el-tooltip
effect="light" effect="light"
:content="menu.meta.title" :content="menu.meta.title"
@ -17,24 +30,40 @@
:offset="-10" :offset="-10"
:disabled="!showTooltip" :disabled="!showTooltip"
> >
<!-- 用于显示菜单标题的 span 元素 -->
<!-- ref="menuTextRef" 为元素设置引用方便在脚本中获取 -->
<!-- @mouseover="hoverMenu" 绑定鼠标移入事件调用 hoverMenu 方法 -->
<span ref="menuTextRef" @mouseover="hoverMenu">{{ <span ref="menuTextRef" @mouseover="hoverMenu">{{
menu.meta.title menu.meta.title
}}</span> }}</span>
</el-tooltip> </el-tooltip>
</template> </template>
<!-- 递归调用 MenuItem 组件传入当前菜单的子菜单列表 -->
<MenuItem :menuList="menu.children" /> <MenuItem :menuList="menu.children" />
</el-sub-menu> </el-sub-menu>
<!-- Element Plus 的菜单选项组件 el-menu-item -->
<!-- v-else 当菜单没有子菜单时渲染该菜单选项 -->
<!-- :index="menu.name + ''" 设置菜单选项的索引 menu.name 转换为字符串类型 -->
<!-- @click="handleMenuClick(menu.name)" 绑定点击事件调用 handleMenuClick 方法并传入菜单的 name -->
<el-menu-item <el-menu-item
:index="menu.name + ''" :index="menu.name + ''"
@click="handleMenuClick(menu.name)" @click="handleMenuClick(menu.name)"
v-else v-else
> >
<!-- 如果菜单有 meta.icon 属性渲染自定义的 SVG 图标组件 svg-icon -->
<svg-icon <svg-icon
v-if="menu.meta.icon" v-if="menu.meta.icon"
:icon="menu.meta.icon" :icon="menu.meta.icon"
class="sub-menu-icon" class="sub-menu-icon"
></svg-icon> ></svg-icon>
<!-- 菜单选项的标题模板 #title -->
<template #title> <template #title>
<!-- Element Plus 的工具提示组件 el-tooltip -->
<!-- effect="light" 设置工具提示的效果为淡色 -->
<!-- :content="menu.meta.title" 绑定工具提示的内容为菜单的标题 -->
<!-- placement="top-start" 设置工具提示显示在目标元素的顶部偏左位置 -->
<!-- :offset="-10" 设置工具提示与目标元素的偏移量为 -10 像素 -->
<!-- :disabled="!showTooltip" 根据 showTooltip 的值决定是否禁用工具提示 -->
<el-tooltip <el-tooltip
effect="light" effect="light"
:content="menu.meta.title" :content="menu.meta.title"
@ -42,9 +71,12 @@
:offset="-10" :offset="-10"
:disabled="!showTooltip" :disabled="!showTooltip"
> >
<!-- 用于显示菜单标题的 span 元素 -->
<!-- ref="menuTextRef" 为元素设置引用方便在脚本中获取 -->
<!-- @mouseover="hoverMenu" 绑定鼠标移入事件调用 hoverMenu 方法 -->
<span ref="menuTextRef" @mouseover="hoverMenu">{{ <span ref="menuTextRef" @mouseover="hoverMenu">{{
menu.meta.title menu.meta.title
}}</span> }}</span>
</el-tooltip> </el-tooltip>
</template> </template>
</el-menu-item> </el-menu-item>
@ -52,26 +84,35 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// Vue defineProps ref
import { defineProps, ref } from 'vue' import { defineProps, ref } from 'vue'
// IRoute
import { IRoute } from '@/router/types' import { IRoute } from '@/router/types'
//
import router from '@/router' import router from '@/router'
// Props menuList IRoute
export interface Props { export interface Props {
menuList: IRoute[] menuList: IRoute[]
} }
// Props
defineProps<Props>() defineProps<Props>()
// showTooltip false
const showTooltip = ref(false) const showTooltip = ref(false)
// handleMenuClick
const handleMenuClick = (name: string) => { const handleMenuClick = (name: string) => {
// 使 router.push name
router.push({ name }) router.push({ name })
} }
// tooltip // hoverMenu
const hoverMenu = (e: Event) => { const hoverMenu = (e: Event) => {
// HTMLSpanElement
const target = e.target as HTMLSpanElement const target = e.target as HTMLSpanElement
// //
showTooltip.value = target.scrollWidth > target.clientWidth showTooltip.value = target.scrollWidth > target.clientWidth
} }
</script> </script>

@ -1,12 +1,25 @@
<template> <template>
<!-- 定义一个具有类名 "sidebar-container" div 元素作为侧边栏的容器 -->
<div class="sidebar-container"> <div class="sidebar-container">
<!-- 定义一个具有类名 "logo-container" div 元素用于放置 logo 和标题 -->
<div class="logo-container"> <div class="logo-container">
<!-- 使用 router-link 组件创建一个到根路径 "/" 的链接标题为 "敬老院管理系统" -->
<router-link title="敬老院管理系统" to="/"> <router-link title="敬老院管理系统" to="/">
<!-- 显示侧边栏 logo 的图片使用相对路径引用项目中的 logo.png 文件 -->
<img class="sidebar-logo" src="@/assets/imgs/logo.png" /> <img class="sidebar-logo" src="@/assets/imgs/logo.png" />
<!-- 显示侧边栏标题的 span 元素 -->
<span class="sidebar-title">敬老院管理系统</span> <span class="sidebar-title">敬老院管理系统</span>
</router-link> </router-link>
</div> </div>
<!-- 使用 Element Plus el-scrollbar 组件为侧边栏添加滚动条 -->
<el-scrollbar> <el-scrollbar>
<!-- 使用 Element Plus el-menu 组件创建侧边栏菜单 -->
<!-- :active-text-color="variables.menuActiveText" 绑定激活菜单项的文本颜色 variables 对象中获取 -->
<!-- :background-color="variables.menuBg" 绑定菜单的背景颜色 variables 对象中获取 -->
<!-- :default-active="active" 绑定默认激活的菜单项通过响应式变量 active 控制 -->
<!-- text-color="#fefefea6" 设置菜单文本的颜色 -->
<!-- unique-opened 确保同一时间只有一个子菜单可以展开 -->
<!-- :collapse="!$store.state.app.siderType" 根据 store app.siderType 的值控制菜单是否折叠 -->
<el-menu <el-menu
:active-text-color="variables.menuActiveText" :active-text-color="variables.menuActiveText"
:background-color="variables.menuBg" :background-color="variables.menuBg"
@ -15,76 +28,120 @@
unique-opened unique-opened
:collapse="!$store.state.app.siderType" :collapse="!$store.state.app.siderType"
> >
<!-- 递归调用 MenuItem 组件传入 store app.routeTree 的值作为菜单列表 -->
<MenuItem :menuList="store.state.app.routeTree" /> <MenuItem :menuList="store.state.app.routeTree" />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
<!-- 定义一个具有类名 "menufold-container" div 元素用于放置菜单折叠按钮 -->
<div class="menufold-container"> <div class="menufold-container">
<!-- 引入并使用 MenuFold 组件 -->
<MenuFold /> <MenuFold />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// components MenuItem
import MenuItem from './components/MenuItem.vue' import MenuItem from './components/MenuItem.vue'
// components MenuFold
import MenuFold from './components/MenuFold.vue' import MenuFold from './components/MenuFold.vue'
// variables.module.scss
import variables from '@/styles/variables.module.scss' import variables from '@/styles/variables.module.scss'
// Vue ref watch
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
// vue-router RouteRecordName useRoute
import { RouteRecordName, useRoute } from 'vue-router' import { RouteRecordName, useRoute } from 'vue-router'
// Vuex
import store from '@/store' import store from '@/store'
//
const route = useRoute() const route = useRoute()
// active 'Home'
let active = ref<RouteRecordName>('Home') let active = ref<RouteRecordName>('Home')
// //
watch( watch(
//
() => route, () => route,
//
newVal => { newVal => {
// name
if (newVal.name) { if (newVal.name) {
// active name
active.value = newVal.name active.value = newVal.name
} }
}, },
{ {
// immediate: true
immediate: true, immediate: true,
// deep: true
deep: true deep: true
} }
) )
</script> </script>
<style lang="scss"> <style lang="scss">
// .logo-container logo
.logo-container { .logo-container {
// router-link
.router-link-active { .router-link-active {
//
display: flex; display: flex;
// 48
height: 48px; height: 48px;
// 100%
width: 100%; width: 100%;
// 使
align-items: center; align-items: center;
// 0 10
padding: 0 10px; padding: 0 10px;
// #002140
background-color: #002140; background-color: #002140;
//
flex-wrap: nowrap; flex-wrap: nowrap;
// .sidebar-logo logo
.sidebar-logo { .sidebar-logo {
// 30
height: 30px; height: 30px;
// 30
width: 30px; width: 30px;
} }
// .sidebar-title
.sidebar-title { .sidebar-title {
//
overflow: hidden; overflow: hidden;
//
text-overflow: ellipsis; text-overflow: ellipsis;
//
white-space: nowrap; white-space: nowrap;
// 8
margin-left: 8px; margin-left: 8px;
// 18
font-size: 18px; font-size: 18px;
//
color: #fff; color: #fff;
} }
} }
} }
// .menufold-container
.menufold-container { .menufold-container {
// 0
bottom: 0; bottom: 0;
// Element Plus 6 -2
box-shadow: 0 0 6px -2px var(--el-color-primary); box-shadow: 0 0 6px -2px var(--el-color-primary);
// 40
height: 40px; height: 40px;
// 40
line-height: 40px; line-height: 40px;
//
position: absolute; position: absolute;
// 100%
width: 100%; width: 100%;
// z-index 999使
z-index: 999; z-index: 999;
} }
</style> </style>

@ -0,0 +1,49 @@
<template>
<!-- SVG 图标容器添加了 svg-icon 类用于样式设置area-hidden="true" 用于隐藏无障碍区域 -->
<svg class="svg-icon" area-hidden="true">
<!-- use 元素用于引用外部 SVG 符号:xlink:href 绑定了动态的图标名称 -->
<use :xlink:href="iconName"></use>
</svg>
</template>
<script setup lang="ts">
// Vue defineProps computed
import { defineProps, computed } from 'vue'
//
const props = defineProps({
// icon
icon: {
type: String,
required: true
},
// size 16
size: {
type: [Number, String],
default: 16
}
})
// icon #icon-
const iconName = computed(() => {
return `#icon-${props.icon}`
})
// size
const iconSize = computed(() => {
return props.size + 'px'
})
</script>
<style lang="scss" scoped>
// .svg-icon SVG
.svg-icon {
// 1em
width: 1em;
// 1em
height: 1em;
//
fill: currentColor;
// iconSize
font-size: v-bind(iconSize);
}
</style>

@ -1,17 +1,30 @@
<template> <template>
<!-- 定义一个具有类名 "tags" div 元素 -->
<div class="tags"> <div class="tags">
<!-- 定义一个一级标题 h1显示文本 "tags" -->
<h1>tags</h1> <h1>tags</h1>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// useTabList
//
// import { useTabList } from '~/composables/useTabList.js' // import { useTabList } from '~/composables/useTabList.js'
// useTabList
// activeTab
// tabList
// changeTab
// removeTab
// handleClose
// const { activeTab, tabList, changeTab, removeTab, handleClose } = useTabList() // const { activeTab, tabList, changeTab, removeTab, handleClose } = useTabList()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .tags "tags"
.tags { .tags {
// 38
height: 38px; height: 38px;
// 0 1 #888
box-shadow: 0 0 1px #888; box-shadow: 0 0 1px #888;
} }
</style> </style>

@ -0,0 +1,118 @@
<template>
<!-- el-dialog Element Plus 中的对话框组件用于展示选择老人的界面 -->
<!-- style="width: 70%" 设置对话框的宽度为父容器的 70% -->
<!-- v-model="dialogVisible" 双向绑定对话框的显示隐藏状态dialogVisible 是一个响应式变量 -->
<!-- title="选择老人" 设置对话框的标题 -->
<!-- destroy-on-close 表示在对话框关闭时销毁其内容 -->
<el-dialog
style="width: 70%"
v-model="dialogVisible"
title="选择老人"
destroy-on-close
>
<!-- 定义一个包含表格的 div 容器 -->
<div class="table-box">
<!-- ProTable 是自定义的表格组件用于展示用户列表 -->
<!-- ref="proTable" 为表格组件添加引用方便在脚本中访问 -->
<!-- title="用户列表" 设置表格的标题 -->
<!-- :columns="columns" 绑定表格的列配置项columns 是一个数组包含每列的信息 -->
<!-- :requestApi="getTableList" 绑定数据请求方法getTableList 用于获取表格数据 -->
<ProTable
ref="proTable"
title="用户列表"
:columns="columns"
:requestApi="getTableList"
>
<!-- 定义表格操作列的插槽内容 -->
<template #operation="scope">
<!-- el-popconfirm Element Plus 中的弹出确认框组件 -->
<!-- title="Are you sure to choose this?" 设置确认框的提示信息 -->
<!-- @confirm="checkElder(scope.row)" 当用户确认时调用 checkElder 方法并传递当前行的数据 -->
<!-- confirm-button-type="warning" 设置确认按钮的类型为警告样式 -->
<el-popconfirm
title="Are you sure to choose this?"
@confirm="checkElder(scope.row)"
confirm-button-type="warning"
>
<!-- 定义确认框的触发元素 -->
<template #reference>
<!-- el-button Element Plus 中的按钮组件 -->
<!-- size="small" 设置按钮大小为小 -->
<!-- link 设置按钮为链接样式 -->
<!-- :icon="View" 绑定图标View 是从 @element-plus/icons-vue 导入的图标组件 -->
<!-- 选择 是按钮的文本内容 -->
<el-button size="small" link :icon="View">选择</el-button>
</template>
</el-popconfirm>
</template>
</ProTable>
</div>
</el-dialog>
</template>
<script setup lang="ts" name="useProTable">
// Vue ref
import { ref } from "vue";
// ProTable ColumnProps
import { ColumnProps } from "@/components/ProTable/interface";
// ProTable
import ProTable from "@/components/ProTable/index.vue";
// Element Plus View
import { View } from "@element-plus/icons-vue";
// dialogVisible
const dialogVisible = ref(false);
// proTable ProTable
const proTable = ref();
// drawerProps DialogProps
const drawerProps = ref<DialogProps>();
// DialogProps elderApi
interface DialogProps {
elderApi: (params: any) => Promise<any>;
}
// getTableList params
//
// elderApi
let getTableList = async (params: any) => {
let newParams = JSON.parse(JSON.stringify(params));
return drawerProps.value?.elderApi(newParams);
};
// columns ColumnProps
const columns: ColumnProps<any>[] = [
{ prop: "rank", label: "序号", width: 55 },
{ prop: "name", label: "姓名", search: { el: "input" } },
{ prop: "idNum", label: "身份证号" },
{ prop: "sex", label: "性别" },
{ prop: "age", label: "年龄" },
{ prop: "phone", label: "电话", search: { el: "input" } },
{ prop: "address", label: "地址" },
{ prop: "operation", label: "操作", width: 70 }
];
// elderAcceptParams DialogProps params
// drawerProps
const elderAcceptParams = (params: DialogProps) => {
drawerProps.value = params;
dialogVisible.value = true;
};
// elderAcceptParams 便
defineExpose({
elderAcceptParams
});
// emit
const emit = defineEmits<{
(event: "getCheckElderInfo", val: any): void
}>();
// checkElder row
// "getCheckElderInfo"
const checkElder = (row: any) => {
emit("getCheckElderInfo", row);
dialogVisible.value = false;
};
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,30 @@
<template>
<!-- 根元素 div应用了多个类名用于设置样式和布局 -->
<!-- class="flex flex-col rounded card-wrap p-3" 表示使用 flex 布局方向为列边框圆角应用 card-wrap 类的样式内边距为 3 -->
<div class="flex flex-col rounded card-wrap p-3">
<!-- 条件渲染的 div如果 title 存在即不是 undefined 或空字符串等假值则显示该 div -->
<!-- class="pb-2" 表示该元素底部外边距为 2 -->
<!-- v-if="title" Vue 的条件渲染指令 -->
<div class="pb-2" v-if="title">{{ title }}</div>
<!-- 插槽用于父组件插入内容会被父组件的内容替换 -->
<slot></slot>
</div>
</template>
<script lang="ts" setup>
// 使 defineProps
// title ?
defineProps<{
title?: string
}>()
</script>
<style lang="scss" scoped>
// .card-wrap
// 1px 线 #eee 18px
.card-wrap {
background-color: #ffffff;
border: 1px solid #eee;
font-size: 18px;
}
</style>

@ -0,0 +1,90 @@
<template>
<!-- Element Plus 的对话框组件 el-dialog -->
<!-- style="width: 70%" 设置对话框的宽度为父容器的 70% -->
<!-- v-model="dialogVisible" 双向绑定对话框的显示隐藏状态dialogVisible 为一个响应式变量 -->
<!-- title="选择床位" 设置对话框的标题为选择床位 -->
<!-- destroy-on-close 表示在对话框关闭时销毁其中的内容 -->
<el-dialog
style="width: 70%"
v-model="dialogVisible"
title="选择床位"
destroy-on-close
>
<!-- Element Plus 的树状组件 el-tree -->
<!-- :data="data" 绑定树状结构的数据data 是一个响应式变量 -->
<!-- :props="defaultProps" 配置树状结构的属性如节点的标识显示文本子节点的字段等 -->
<!-- accordion 使树状结构以手风琴模式显示即同一时间只有一个节点可以展开 -->
<!-- @node-click="checkBed" 监听节点点击事件当点击节点时调用 checkBed 函数 -->
<el-tree
:data="data"
:props="defaultProps"
accordion
@node-click="checkBed"
/>
</el-dialog>
</template>
<script setup lang="ts" name="useProTable">
// Vue ref
import { ref } from "vue";
// API getBuildTree
import { getBuildTree } from "@/apis/bookManage";
// data undefined
const data: any = ref();
// dialogVisible false
const dialogVisible = ref(false);
// drawerProps DialogProps undefined
const drawerProps = ref<DialogProps>();
// DialogProps
// treeApi PromisePromise
interface DialogProps {
treeApi: (params: any) => Promise<any>;
}
// defaultProps el-tree
// id: "id" "id"
// label: "name" "name"
// children: "childrenList" "childrenList"
const defaultProps = {
id: "id",
label: "name",
children: "childrenList"
};
// treeAcceptParams
const treeAcceptParams = async (params: DialogProps) => {
// drawerProps
drawerProps.value = params;
//
dialogVisible.value = true;
// getBuildTree
const res: any = await getBuildTree();
// data
data.value = res.data;
};
// treeAcceptParams 便
defineExpose({
treeAcceptParams
});
// "getCheckBedInfo"
//
const emit = defineEmits<{
(event: "getCheckBedInfo", val: any): void
}>();
// checkBed
const checkBed = (bed: any) => {
// level 4
if (bed.level === 4) {
// "getCheckBedInfo"
emit("getCheckBedInfo", bed);
//
dialogVisible.value = false;
}
};
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,189 @@
<template>
<!-- Element Plus 的上传组件 el-upload -->
<!-- v-model:file-list="imageList" 双向绑定上传文件列表imageList 是一个响应式变量 -->
<!-- :action="requestUrl" 设置上传文件的接口地址requestUrl 是一个变量 -->
<!-- :headers="{ token: token }" 设置上传请求的头部信息包含 token -->
<!-- list-type="picture-card" 设置上传文件列表的展示类型为图片卡片形式 -->
<!-- :before-upload="uploadBefore" 在文件上传前调用 uploadBefore 函数进行处理 -->
<!-- :on-error="handleError" 当文件上传失败时调用 handleError 函数 -->
<!-- :on-success="handleSuccess" 当文件上传成功时调用 handleSuccess 函数 -->
<!-- :on-preview="handlePreview" 当点击预览图片时调用 handlePreview 函数 -->
<!-- :on-remove="handleRemove" 当移除上传文件时调用 handleRemove 函数 -->
<el-upload
v-model:file-list="imageList"
:action="requestUrl"
:headers="{ token: token }"
list-type="picture-card"
:before-upload="uploadBefore"
:on-error="handleError"
:on-success="handleSuccess"
:on-preview="handlePreview"
:on-remove="handleRemove"
>
<!-- Element Plus 的图标组件 el-icon展示一个加号图标 -->
<el-icon>
<Plus />
</el-icon>
</el-upload>
<!-- Element Plus 的对话框组件 el-dialog用于展示预览图片 -->
<!-- v-model="dialogVisible" 双向绑定对话框的显示隐藏状态dialogVisible 是一个响应式变量 -->
<el-dialog v-model="dialogVisible">
<!-- img 标签用于显示预览图片w-full 可能是一个自定义的类名表示宽度占满:src="dialogImageUrl" 绑定图片的源地址dialogImageUrl 是一个响应式变量 -->
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
<!--
TODO 上传图片 / 使用方法
// 1.
import elderListDialog from "@/components/elderListDialog/index.vue";
// 2.使
<uploadImage :uploadParams="uploadParams" @setImageData='setImageData' />
// 3. single / multiple
ref({
uploadType: "single",
imageList: ref<UploadUserFile[]>([])
});
const setImageData = (url: string) => {
formData.value.picture = url;
};
// 4.()
//
uploadParams.value.imageList = []
//
uploadParams.value.imageList.push({
uid: 0,
name: "0.jpeg",
url: formData.value.image
});
-->
<script lang="ts" setup>
// Vuex store
import store from "@/store";
// URL
import { baseUrl } from "@/utils/http";
// Element Plus ElMessage
import { ElMessage } from "element-plus";
// Element Plus Plus
import { Plus } from "@element-plus/icons-vue";
// Vue
import { defineEmits, onMounted, ref, watch } from "vue";
// Element Plus
import type { UploadUserFile, ElUpload } from "element-plus";
// dialogImageUrl URL
const dialogImageUrl = ref("");
// dialogVisible false
const dialogVisible = ref(false);
// imageList
const imageList = ref<UploadUserFile[]>([]);
// props uploadParams
const props = defineProps({
uploadParams: Object
});
//
onMounted(() => {
// uploadParams imageList imageList
props.uploadParams?.["imageList"].forEach((image: any) => {
imageList.value.push(image);
});
});
// URL
const requestUrl = baseUrl + "file/uploadImg";
// Vuex store token
const token = store.state.app.token;
// imageUrlList URL
const imageUrlList = ref<any[]>([]);
// "setImageData"
const emits = defineEmits(["setImageData"]);
// updateData
const updateData = () => {
// imageUrlList
imageUrlList.value = [];
// imageList URL imageUrlList
imageList.value.forEach(image => imageUrlList.value.push(image.url));
// data
const data = ref<any>();
//
if (props.uploadParams?.["uploadType"] === "single") {
// URL data
data.value = imageUrlList.value[0];
} else {
// imageUrlList data
data.value = imageUrlList;
}
// "setImageData"
emits("setImageData", data);
};
// imageList
watch(imageList, (value, oldValue, onCleanup) => {
// imageList 1
if (props.uploadParams?.["uploadType"] === "single" && imageList.value.length > 1) {
//
imageList.value.splice(0, 1);
//
updateData();
}
});
// uploadBefore
const uploadBefore = (file: any) => {
// jpeg png
const isJPG = file.type === "image/jpeg" || file.type === "image/png";
// 2MB
const isLt2M = file.size / 1024 / 1024 < 2;
// jpeg png
if (!isJPG) {
ElMessage.error("只能上传jpg/png文件");
}
// 2MB
if (!isLt2M) {
ElMessage.error("文件大小不能超过2MB");
}
//
return isJPG && isLt2M;
};
// handleError
const handleError = (response: any) => {
//
ElMessage.error(response.data.msg);
};
// handleSuccess
const handleSuccess = (response: any, uploadFile: any) => {
// URL url
uploadFile.url = response.data.url;
//
updateData();
};
// handleRemove
const handleRemove = async (uploadFile: any) => {
// imageList
imageList.value.filter((image) => image.uid!== uploadFile.uid);
//
updateData();
// TODO imageId : uploadFile.uid
};
// handlePreview
const handlePreview = (uploadFile: any) => {
// URL URL
dialogImageUrl.value = uploadFile.url!;
//
dialogVisible.value = true;
};
</script>

@ -0,0 +1,47 @@
<template>
<div>
<!-- 定义一个 Element Plus 的按钮点击该按钮会触发 addNew 方法 -->
<el-button @click="addNew"></el-button>
<!-- 使用 v-for 指令遍历 componentList 数组将数组中的每个元素渲染为一个组件 -->
<div v-for="(item, index) in componentList" :key="index">
<!-- 使用 el-component 动态添加组件 -->
<!-- :is="item.type" 指定要渲染的组件类型该类型从 componentList 数组元素的 type 属性获取 -->
<!-- :props="item.props" 传递组件所需的 props 数据这些数据从 componentList 数组元素的 props 属性获取 -->
<el-component :is="item.type" :props="item.props"></el-component>
</div>
</div>
</template>
<script>
// HelloWorld
import HelloWorld from './components/HelloWorld.vue'
export default {
// data
data() {
return {
// componentList
componentList: []
}
},
// methods
methods: {
// addNew componentList
addNew() {
// componentList
// type HelloWorld
// props HelloWorld msg 'Hello World!'
this.componentList.push({
type: HelloWorld,
props: {
msg: 'Hello World!'
}
})
}
},
// components HelloWorld
components: {
HelloWorld
}
}
</script>

@ -0,0 +1,42 @@
// 定义一个名为 Table 的命名空间
export namespace Table {
// 在 Table 命名空间内,定义一个接口 Pageable用于描述分页相关的属性
export interface Pageable {
// 当前页码,类型为数字
pageNum: number
// 每页显示的数量,类型为数字
pageSize: number
// 数据的总数量,类型为数字
total: number
}
// 在 Table 命名空间内,定义一个接口 TableStateProps用于描述表格相关的状态属性
export interface TableStateProps {
// 表格数据,是一个任意类型的数组
tableData: any[]
// 分页相关的属性,类型为 Pageable 接口
pageable: Pageable
// 搜索参数,是一个键值对对象,键为字符串,值为任意类型
searchParam: {
[key: string]: any
}
// 初始化的搜索参数,是一个键值对对象,键为字符串,值为任意类型
searchInitParam: {
[key: string]: any
}
// 用于获取总数的参数,是一个键值对对象,键为字符串,值为任意类型
totalParam: {
[key: string]: any
}
// 可选的图标相关属性,是一个键值对对象,键为字符串,值为任意类型,默认为 undefined
icon?: {
[key: string]: any
}
}
}
// 定义一个名为 HandleData 的命名空间
export namespace HandleData {
// 在 HandleData 命名空间内,定义一个类型别名 MessageType
// MessageType 的值可以是空字符串,或者是'success'、'warning'、'info'、'error' 中的一个
export type MessageType = '' |'success' | 'warning' | 'info' | 'error'
}

@ -0,0 +1,52 @@
// 从 element-plus 库中导入 ElMessageBox 和 ElMessage 组件,分别用于显示确认框和提示消息
import { ElMessageBox, ElMessage } from 'element-plus'
// 从 './interface' 文件中导入 HandleData 命名空间,用于获取其中定义的类型
import { HandleData } from './interface'
/**
* @description ()
* @param {Function} api api()
* @param {Object} params {id,params}()
* @param {String} message ()
* @param {String} confirmType icon(, warning)
* @return Promise
*/
// 定义一个泛型函数 useHandleDataP 表示 api 函数参数的类型R 表示 api 函数返回值的类型
export const useHandleData = <P = any, R = any>(
// api 是一个函数,接收类型为 P 的参数并返回一个 PromisePromise 的解析值类型为 R
api: (params: P) => Promise<R>,
// params 是 api 函数的参数,类型与 api 函数的参数类型一致
params: Parameters<typeof api>[0],
// message 是一个字符串,用于显示确认框中的提示信息
message: string,
// confirmType 是一个字符串,类型为 HandleData.MessageType默认值为 'warning'
confirmType: HandleData.MessageType = 'warning'
) => {
// 返回一个 Promise
return new Promise((resolve, reject) => {
// 显示一个确认框
ElMessageBox.confirm(`是否${message}?`, '温馨提示', {
// 确认按钮的文本
confirmButtonText: '确定',
// 取消按钮的文本
cancelButtonText: '取消',
// 确认框的图标类型
type: confirmType,
// 是否可拖动
draggable: true
})
.then(async () => {
// 调用 api 函数并等待其返回结果
const res = await api(params)
// 如果结果为 falsy 值,则 reject 该 Promise
if (!res) return reject(false)
// 显示一个成功提示消息
ElMessage({
type:'success',
message: `${message}成功!`
})
// resolve 该 Promise表示操作成功
resolve(true)
})
})
}

@ -0,0 +1,25 @@
// 从 '@/components/SvgIcon/index.vue' 导入 SvgIcon 组件
import SvgIcon from '@/components/SvgIcon/index.vue'
// 从 'vue' 导入 h 函数和 defineComponent 函数
import { h, defineComponent } from 'vue'
// 定义一个名为 useRenderIcon 的函数它接受两个参数iconName字符串类型和 attrs可选的任意类型
export function useRenderIcon(iconName: string, attrs?: any) {
// 使用 defineComponent 函数定义一个新的组件
return defineComponent({
// 新组件的名称为 'SvgIcon'
name: 'SvgIcon',
// 定义组件的渲染函数
render() {
// 使用 h 函数创建一个 SvgIcon 组件的虚拟节点
// 第一个参数是要创建的组件(这里是 SvgIcon
// 第二个参数是一个对象,包含要传递给 SvgIcon 组件的属性
// 'icon' 属性的值是传入的 iconName
// '...attrs' 表示将 attrs 对象中的所有属性也传递给 SvgIcon 组件
return h(SvgIcon, {
icon: iconName,
...attrs
})
}
})
}

@ -0,0 +1,51 @@
import { ref, computed } from 'vue'
/**
* @description
* @param {String} rowKey id
* */
// 定义一个名为 useSelection 的函数,用于处理表格多选数据的操作
// 接受一个可选参数 rowKey默认值为 'id',表示行数据中用于唯一标识的键名
export const useSelection = (rowKey = 'id') => {
// 创建一个响应式变量 isSelected用于表示是否选中了数据
// 初始值为 false类型为 boolean
const isSelected = ref<boolean>(false)
// 创建一个响应式变量 selectedList用于存储选中的数据列表
// 初始值为空数组,类型为 any[]
const selectedList = ref([])
// 创建一个计算属性 selectedListIds用于获取当前选中的所有数据的 id 数组
// 计算属性会根据依赖项(这里是 selectedList的变化而自动更新
const selectedListIds = computed((): string[] => {
// 初始化一个空数组 ids 用于存储 id
const ids: string[] = []
// 遍历 selectedList 中的每一项数据
selectedList.value.forEach(item => ids.push(item[rowKey]))
// 返回包含所有 id 的数组
return ids
})
/**
* @description
* @param {Array} rowArr
* @return void
*/
// 定义一个函数 selectionChange用于处理表格多选数据的变化事件
// 接受一个参数 rowArr类型为 any[],表示当前选中的所有数据
const selectionChange = (rowArr: any) => {
// 如果选中的数据列表为空,则将 isSelected 设置为 false
// 否则将 isSelected 设置为 true
rowArr.length === 0? (isSelected.value = false) : (isSelected.value = true)
// 更新 selectedList 为当前选中的数据列表
selectedList.value = rowArr
}
// 返回一个包含 isSelected、selectedList、selectedListIds 和 selectionChange 的对象
// 以便在其他地方使用这些变量和函数来处理表格多选数据
return {
isSelected,
selectedList,
selectedListIds,
selectionChange
}
}

@ -0,0 +1,98 @@
// import { ref } from 'vue'
// import { useRoute, onBeforeRouteUpdate } from 'vue-router'
// import { useCookies } from '@vueuse/integrations/useCookies'
// import { router } from '~/router'
// export function useTabList() {
// const route = useRoute()
// const cookie = useCookies()
// const activeTab = ref(route.path)
// const tabList = ref([
// {
// title: '后台首页',
// path: '/'
// }
// ])
// // 添加标签导航
// function addTab(tab) {
// let noTab = tabList.value.findIndex((t) => t.path == tab.path) == -1
// if (noTab) {
// tabList.value.push(tab)
// }
// cookie.set('tabList', tabList.value)
// }
// // 初始化标签导航列表
// function initTabList() {
// let tbs = cookie.get('tabList')
// if (tbs) {
// tabList.value = tbs
// }
// }
// initTabList()
// onBeforeRouteUpdate((to, from) => {
// activeTab.value = to.path
// addTab({
// title: to.meta.title,
// path: to.path
// })
// })
// const changeTab = (t) => {
// activeTab.value = t
// router.push(t)
// }
// const removeTab = (t) => {
// let tabs = tabList.value
// let a = activeTab.value
// if (a == t) {
// tabs.forEach((tab, index) => {
// if (tab.path == t) {
// const nextTab = tabs[index + 1] || tabs[index - 1]
// if (nextTab) {
// a = nextTab.path
// }
// }
// })
// }
// activeTab.value = a
// tabList.value = tabList.value.filter((tab) => tab.path != t)
// cookie.set('tabList', tabList.value)
// }
// const handleClose = (c) => {
// if (c == 'clearAll') {
// // 切换回首页
// activeTab.value = '/'
// // 过滤只剩下首页
// tabList.value = [
// {
// title: '后台首页',
// path: '/'
// }
// ]
// } else if (c == 'clearOther') {
// // 过滤只剩下首页和当前激活
// tabList.value = tabList.value.filter(
// (tab) => tab.path == '/' || tab.path == activeTab.value
// )
// }
// cookie.set('tabList', tabList.value)
// }
// return {
// activeTab,
// tabList,
// changeTab,
// removeTab,
// handleClose
// }
// }

@ -0,0 +1,210 @@
import { Table } from './interface'
import { reactive, computed, toRefs } from 'vue'
/**
* @description table
* @param {Function} api api ()
* @param {Object} initParam ({})
* @param {Boolean} isPageable (true)
* @param {Function} dataCallBack ()
* */
// 定义一个名为 useTable 的函数,用于封装表格页面的操作方法
// api 是获取表格数据的 API 方法,是必传参数
// initParam 是获取数据的初始化参数,非必传,默认值为空对象
// isPageable 表示是否有分页功能,非必传,默认值为 true
// dataCallBack 是对后台返回的数据进行处理的方法,非必传
export const useTable = (
api: (params: any) => Promise<any>,
initParam: object = {},
isPageable = true,
dataCallBack?: (data: any) => any
) => {
// 使用 reactive 创建一个响应式的 state 对象,包含表格数据、分页数据、查询参数等
const state = reactive<Table.TableStateProps>({
// 表格数据,初始值为空数组
tableData: [],
// 分页数据,包含当前页数、每页显示条数和总条数
pageable: {
// 当前页数,初始值为 1
pageNum: 1,
// 每页显示条数,初始值为 10
pageSize: 10,
// 总条数,初始值为 0
total: 0
},
// 查询参数,只包括查询条件,初始值为空对象
searchParam: {},
// 初始化默认的查询参数,初始值为空对象
searchInitParam: {},
// 总参数,包含分页和查询参数,初始值为空对象
totalParam: {}
})
/**
* @description (,)
* */
// 定义一个计算属性 pageParam用于获取和设置分页查询参数
const pageParam = computed({
// 获取分页查询参数
get: () => {
return {
pageNum: state.pageable.pageNum,
pageSize: state.pageable.pageSize
}
},
// 设置分页查询参数,这里暂时为空,可根据需要实现
set: (newVal: any) => { // 我是分页更新之后的值
}
})
/**
* @description
* @return void
* */
// 定义一个异步函数 getTableList用于获取表格数据
const getTableList = async () => {
try {
// 先把初始化参数和分页参数放到总参数里面
Object.assign(
state.totalParam,
initParam,
isPageable? pageParam.value : {}
)
// 请求前格式化数据,如果总参数中有 consultDate 字段,则进行处理
if (state.totalParam.consultDate) {
state.totalParam.startTime = state.totalParam.consultDate[0]
state.totalParam.endTime = state.totalParam.consultDate[1]
delete state.totalParam.consultDate
}
// 调用 API 方法获取数据
let { data } = await api({
...state.searchInitParam,
...state.totalParam
})
// 如果有数据处理回调函数,则对数据进行处理
dataCallBack && (data = dataCallBack(data))
// 获取当前表格数据,如果有分页,则取 data.list否则取 data
state.tableData = isPageable? data.list : data
// 从返回的数据中获取分页信息
const { pageNum, pageSize, total } = data
// 如果有分页,则更新分页信息
isPageable && updatePageable({ pageNum, pageSize, total })
} catch (error) {
// 如果发生错误,打印错误信息
console.log(error)
}
}
/**
* @description
* @return void
* */
// 定义一个函数 updatedTotalParam用于更新总参数
const updatedTotalParam = () => {
state.totalParam = {}
// 处理查询参数,可以给查询参数加自定义前缀操作
const nowSearchParam: { [key: string]: any } = {}
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
for (const key in state.searchParam) {
// * 某些情况下参数为 false/0 也应该携带参数
if (
state.searchParam[key] ||
state.searchParam[key] === false ||
state.searchParam[key] === 0
) {
nowSearchParam[key] = state.searchParam[key]
}
}
// 将处理后的查询参数和分页参数合并到总参数中
Object.assign(
state.totalParam,
nowSearchParam,
isPageable? pageParam.value : {}
)
}
/**
* @description
* @param {Object} resPageable
* @return void
* */
// 定义一个函数 updatePageable用于更新分页信息
const updatePageable = (resPageable: Table.Pageable) => {
// 使用 Object.assign 方法将后台返回的分页数据合并到 state.pageable 中
Object.assign(state.pageable, resPageable)
}
/**
* @description
* @return void
* */
// 定义一个函数 search用于进行表格数据查询
const search = () => {
// 将当前页数设置为 1
state.pageable.pageNum = 1
// 更新总参数
updatedTotalParam()
// 获取表格数据
getTableList()
}
/**
* @description
* @return void
* */
// 定义一个函数 reset用于重置表格数据
const reset = () => {
// 将当前页数设置为 1
state.pageable.pageNum = 1
// 清空搜索参数
state.searchParam = {}
// 重置搜索表单时,如果有默认搜索参数,则重置为默认值
Object.keys(state.searchInitParam).forEach(key => {
state.searchParam[key] = state.searchInitParam[key]
})
// 更新总参数
updatedTotalParam()
// 获取表格数据
getTableList()
}
/**
* @description
* @param {Number} val
* @return void
* */
// 定义一个函数 handleSizeChange用于处理每页条数改变的事件
const handleSizeChange = (val: number) => {
// 将当前页数设置为 1
state.pageable.pageNum = 1
// 更新每页显示条数
state.pageable.pageSize = val
// 获取表格数据
getTableList()
}
/**
* @description
* @param {Number} val
* @return void
* */
// 定义一个函数 handleCurrentChange用于处理当前页改变的事件
const handleCurrentChange = (val: number) => {
// 更新当前页数
state.pageable.pageNum = val
// 获取表格数据
getTableList()
}
// 返回一个包含 state 的响应式引用、获取表格数据的函数、查询函数、重置函数、处理每页条数改变和当前页改变的函数以及更新总参数的函数
return {
...toRefs(state),
getTableList,
search,
reset,
handleSizeChange,
handleCurrentChange,
updatedTotalParam
}
}

@ -1,19 +1,32 @@
<template> <template>
<!-- Element Plus 的容器组件 el-container添加了自定义类名 "layout-container" -->
<!-- :class="{ hideSidebar: !$store.state.app.siderType }" 根据 store app.siderType 的值动态添加 "hideSidebar" 类名 -->
<el-container <el-container
class="layout-container" class="layout-container"
:class="{ hideSidebar: !$store.state.app.siderType }" :class="{ hideSidebar: !$store.state.app.siderType }"
> >
<el-aside :width="$store.state.app.siderType ? '230px' : '64px'"> <!-- Element Plus 的侧边栏组件 el-aside -->
<!-- :width="$store.state.app.siderType? '230px' : '64px'" 根据 store app.siderType 的值动态设置侧边栏宽度 -->
<el-aside :width="$store.state.app.siderType? '230px' : '64px'">
<!-- 引入并使用自定义的 SideBar 组件 -->
<SideBar /> <SideBar />
</el-aside> </el-aside>
<!-- Element Plus 的容器组件 el-container添加了自定义类名 "main-container" -->
<el-container class="main-container"> <el-container class="main-container">
<!-- Element Plus 的头部组件 el-header -->
<el-header> <el-header>
<!-- 引入并使用自定义的 NavBar 组件 -->
<NavBar /> <NavBar />
</el-header> </el-header>
<!-- Element Plus 的主体组件 el-main -->
<el-main> <el-main>
<!-- Element Plus 的滚动条组件 el-scrollbar -->
<el-scrollbar> <el-scrollbar>
<!-- 路由视图 router-view使用插槽获取当前要渲染的组件 Component -->
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<!-- 过渡组件 transition设置过渡名称为 "fade-transform"过渡模式为 "out-in" -->
<transition name="fade-transform" mode="out-in"> <transition name="fade-transform" mode="out-in">
<!-- 动态组件 component根据 Component 的值渲染对应的组件 -->
<component :is="Component" /> <component :is="Component" />
</transition> </transition>
</router-view> </router-view>
@ -24,10 +37,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// SideBar
import SideBar from './components/SideBar/index.vue' import SideBar from './components/SideBar/index.vue'
// NavBar
import NavBar from './components/NavBar/index.vue' import NavBar from './components/NavBar/index.vue'
// Vuex
import store from '@/store' import store from '@/store'
//
// window.onresize = () => // window.onresize = () =>
// (() => { // (() => {
// /** width app-wrapper // /** width app-wrapper
@ -36,8 +53,10 @@ import store from '@/store'
// * width > 990 // * width > 990
// */ // */
// // body
// let width = document.body.clientWidth // let width = document.body.clientWidth
// // mutation
// if (width > 0 && width <= 760) { // // if (width > 0 && width <= 760) { //
// store.commit('app/setDeviceType', 'phone') // store.commit('app/setDeviceType', 'phone')
// } else if (width > 760 && width <= 990) { // // } else if (width > 760 && width <= 990) { //
@ -49,27 +68,41 @@ import store from '@/store'
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// .layout-container
.layout-container { .layout-container {
//
position: relative; position: relative;
// 100%
width: 100%; width: 100%;
// 100%
height: 100%; height: 100%;
} }
// .el-header Element Plus
// //
.el-header { .el-header {
//
position: relative; position: relative;
// 0!important
padding: 0 !important; padding: 0 !important;
// 48 !important
height: 48px !important; height: 48px !important;
} }
// .el-main Element Plus
// //
.el-main { .el-main {
// 0!important
padding: 0 !important; padding: 0 !important;
// #f0f2f5
background-color: #f0f2f5; background-color: #f0f2f5;
} }
// .el-scrollbar Element Plus
.el-scrollbar { .el-scrollbar {
// rgb(246, 246, 246)!important
background-color: rgb(246, 246, 246) !important; background-color: rgb(246, 246, 246) !important;
// 10px
padding: 10px; padding: 10px;
} }
</style> </style>

@ -1,15 +1,23 @@
// 从当前目录的 index 文件中导入 routes、users、IRoute 和 IUser
import { routes, users, IRoute, IUser } from './index' import { routes, users, IRoute, IUser } from './index'
// 导入项目的 Vuex 存储实例
import store from '@/store' import store from '@/store'
// 获取路由列表 // 获取路由列表的函数,接受一个用户 IDuid作为参数
export const getRouterList = (uid: number) => { export const getRouterList = (uid: number) => {
// 从 Vuex 存储中获取当前用户的权限 ID 列表
const authIdList: number[] = store.state.app.userPeofile.authIdList const authIdList: number[] = store.state.app.userPeofile.authIdList
// 过滤出当前用户有权限访问的路由列表
const routeList: any[] = routes.filter(route => authIdList.includes(route.id)) const routeList: any[] = routes.filter(route => authIdList.includes(route.id))
// 如果传入了用户 IDuid
if (uid) { if (uid) {
// userInfo 有可能是 undefined // 查找具有指定 ID 的用户信息,userInfo 有可能是 undefined
const userInfo: IUser | undefined = users.find(user => user.id === uid) const userInfo: IUser | undefined = users.find(user => user.id === uid)
// 如果找到了用户信息
if (userInfo) { if (userInfo) {
// 以下是一段被注释掉的代码,功能可能是进一步筛选用户的授权路由列表
// const authRouteList: IRoute[] = [] // const authRouteList: IRoute[] = []
// rid = router id // rid = router id
// userInfo.auth.map(rid => { // userInfo.auth.map(rid => {
@ -19,12 +27,14 @@ export const getRouterList = (uid: number) => {
// } // }
// }) // })
// }) // })
// 返回成功响应,包含状态码、消息和筛选后的路由列表
return { return {
code: 0, code: 0,
msg: 'ok', msg: 'ok',
data: routeList data: routeList
} }
} else { } else {
// 如果没有找到用户信息,返回错误响应
return { return {
code: 1001, code: 1001,
msg: 'No userInfo for this UID', msg: 'No userInfo for this UID',
@ -32,6 +42,7 @@ export const getRouterList = (uid: number) => {
} }
} }
} else { } else {
// 如果没有传入用户 ID返回错误响应
return { return {
code: 1002, code: 1002,
msg: 'No UID received', msg: 'No UID received',

@ -1,5 +1,9 @@
// 从当前目录下的 routes 文件中导入 routes 变量和 IRoute 类型
import routes, { IRoute } from './routes' import routes, { IRoute } from './routes'
// 从当前目录下的 users 文件中导入 users 变量和 IUser 类型
import users, { IUser } from './users' import users, { IUser } from './users'
// 导出 routes 变量,这样在其他文件中可以导入使用该变量
export { routes, users } export { routes, users }
// 导出 IRoute 和 IUser 类型,方便其他文件导入使用这些类型定义
export type { IRoute, IUser } export type { IRoute, IUser }

@ -1,17 +1,29 @@
// 定义一个接口 IRoute用于描述路由的结构
export interface IRoute { export interface IRoute {
// 路由的唯一标识 ID类型为数字
id: number id: number
// 父路由的 ID类型为数字
pid: number pid: number
// 路由的名称,类型为字符串
name: string name: string
// 路由的路径,类型为字符串
path: string path: string
// 重定向的路径,是一个可选属性,类型为字符串
redirect?: string redirect?: string
// 路由对应的组件路径,类型为字符串
component: string component: string
// 路由的元数据,是一个对象
meta: { meta: {
// 路由的标题,类型为字符串
title: string title: string
// 路由的图标名称,是一个可选属性,类型为字符串
icon?: string icon?: string
} }
} }
// 定义一个名为 routes 的数组,元素类型为 IRoute存储路由信息
const routes: IRoute[] = [ const routes: IRoute[] = [
// 第一个路由对象
{ {
id: 1, id: 1,
pid: 0, pid: 0,
@ -23,6 +35,7 @@ const routes: IRoute[] = [
icon: 'home' icon: 'home'
} }
}, },
// 第二个路由对象
{ {
id: 2, id: 2,
pid: 0, pid: 0,
@ -35,39 +48,43 @@ const routes: IRoute[] = [
icon: 'sale' icon: 'sale'
} }
}, },
// 第三个路由对象
{ {
id: 3, id: 3,
pid: 2, pid: 2,
path: 'counsel', path: 'counsel',
name: 'CounselSale', name: 'CounselSale',
component: 'sale/counsel/index.vue', component:'sale/counsel/index.vue',
meta: { meta: {
title: '咨询管理', title: '咨询管理',
icon: '' icon: ''
} }
}, },
// 以下是一些被注释掉的路由对象,可根据需求启用
// { // {
// id: 4, // id: 4,
// pid: 2, // pid: 2,
// path: 'intention', // path: 'intention',
// name: 'IntentionSale', // name: 'IntentionSale',
// component: 'sale/intention/index.vue', // component:'sale/intention/index.vue',
// meta: { // meta: {
// title: '意向客户', // title: '意向客户',
// icon: '' // icon: ''
// } // }
// }, // },
// 第五个路由对象
{ {
id: 5, id: 5,
pid: 2, pid: 2,
path: 'book', path: 'book',
name: 'BookSale', name: 'BookSale',
component: 'sale/book/index.vue', component:'sale/book/index.vue',
meta: { meta: {
title: '预定管理', title: '预定管理',
icon: '' icon: ''
} }
}, },
// 第六个路由对象
{ {
id: 6, id: 6,
pid: 0, pid: 0,
@ -80,6 +97,7 @@ const routes: IRoute[] = [
icon: 'live' icon: 'live'
} }
}, },
// 第七个路由对象
{ {
id: 7, id: 7,
pid: 6, pid: 6,
@ -91,6 +109,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第八个路由对象
{ {
id: 8, id: 8,
pid: 6, pid: 6,
@ -102,6 +121,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第九个路由对象
{ {
id: 9, id: 9,
pid: 6, pid: 6,
@ -113,6 +133,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第十个路由对象
{ {
id: 10, id: 10,
pid: 6, pid: 6,
@ -124,6 +145,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第十一个路由对象
{ {
id: 11, id: 11,
pid: 6, pid: 6,
@ -135,6 +157,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第十二个路由对象
{ {
id: 12, id: 12,
pid: 6, pid: 6,
@ -146,6 +169,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第十三个路由对象
{ {
id: 13, id: 13,
pid: 0, pid: 0,
@ -158,6 +182,7 @@ const routes: IRoute[] = [
icon: 'people' icon: 'people'
} }
}, },
// 第十四个路由对象
{ {
id: 14, id: 14,
pid: 13, pid: 13,
@ -169,10 +194,11 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第十五个路由对象
{ {
id: 15, id: 15,
pid: 13, pid: 13,
path: 'staff', path:'staff',
name: 'StaffPeople', name: 'StaffPeople',
component: 'people/staff/index.vue', component: 'people/staff/index.vue',
meta: { meta: {
@ -180,6 +206,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 以下是一些被注释掉的路由对象,可根据需求启用
// { // {
// id: 16, // id: 16,
// pid: 13, // pid: 13,
@ -191,61 +218,66 @@ const routes: IRoute[] = [
// icon: '' // icon: ''
// } // }
// }, // },
// 第十七个路由对象
{ {
id: 17, id: 17,
pid: 0, pid: 0,
path: '/serve', path: '/serve',
name: 'Serve', name: 'Serve',
redirect: '/serve/project', redirect: '/serve/project',
component: 'serve/index.vue', component:'serve/index.vue',
meta: { meta: {
title: '服务管理', title: '服务管理',
icon: 'serve' icon:'serve'
} }
}, },
// 第十八个路由对象
{ {
id: 18, id: 18,
pid: 17, pid: 17,
path: 'project', path: 'project',
name: 'ProjectServe', name: 'ProjectServe',
component: 'serve/project/index.vue', component:'serve/project/index.vue',
meta: { meta: {
title: '服务项目', title: '服务项目',
icon: '' icon: ''
} }
}, },
// 第十九个路由对象
{ {
id: 19, id: 19,
pid: 17, pid: 17,
path: 'level', path: 'level',
name: 'LevelServe', name: 'LevelServe',
component: 'serve/level/index.vue', component:'serve/level/index.vue',
meta: { meta: {
title: '护理等级', title: '护理等级',
icon: '' icon: ''
} }
}, },
// 第二十个路由对象
{ {
id: 20, id: 20,
pid: 17, pid: 17,
path: 'book', path: 'book',
name: 'BookServe', name: 'BookServe',
component: 'serve/book/index.vue', component:'serve/book/index.vue',
meta: { meta: {
title: '服务预定', title: '服务预定',
icon: '' icon: ''
} }
}, },
// 以下是一些被注释掉的路由对象,可根据需求启用
// { // {
// id: 21, // id: 21,
// pid: 0, // pid: 0,
// path: '/resource', // path: '/resource',
// name: 'Resource', // name: 'Resource',
// redirect: '/resource/info', // redirect: '/resource/info',
// component: 'resource/index.vue', // component:'resource/index.vue',
// meta: { // meta: {
// title: '物资管理', // title: '物资管理',
// icon: 'resource' // icon:'resource'
// } // }
// }, // },
// { // {
@ -253,7 +285,7 @@ const routes: IRoute[] = [
// pid: 21, // pid: 21,
// path: 'info', // path: 'info',
// name: 'InfoResource', // name: 'InfoResource',
// component: 'resource/info/index.vue', // component:'resource/info/index.vue',
// meta: { // meta: {
// title: '物资信息', // title: '物资信息',
// icon: '' // icon: ''
@ -264,7 +296,7 @@ const routes: IRoute[] = [
// pid: 21, // pid: 21,
// path: 'Storage', // path: 'Storage',
// name: 'StorageResource', // name: 'StorageResource',
// component: 'resource/storage/index.vue', // component:'resource/storage/index.vue',
// meta: { // meta: {
// title: '仓库设置', // title: '仓库设置',
// icon: '' // icon: ''
@ -275,7 +307,7 @@ const routes: IRoute[] = [
// pid: 21, // pid: 21,
// path: 'enter', // path: 'enter',
// name: 'EnterResource', // name: 'EnterResource',
// component: 'resource/enter/index.vue', // component:'resource/enter/index.vue',
// meta: { // meta: {
// title: '入库管理', // title: '入库管理',
// icon: '' // icon: ''
@ -286,7 +318,7 @@ const routes: IRoute[] = [
// pid: 21, // pid: 21,
// path: 'leave', // path: 'leave',
// name: 'LeaveResource', // name: 'LeaveResource',
// component: 'resource/leave/index.vue', // component:'resource/leave/index.vue',
// meta: { // meta: {
// title: '出库管理', // title: '出库管理',
// icon: '' // icon: ''
@ -295,14 +327,15 @@ const routes: IRoute[] = [
// { // {
// id: 26, // id: 26,
// pid: 21, // pid: 21,
// path: 'search', // path:'search',
// name: 'SearchResource', // name: 'SearchResource',
// component: 'resource/search/index.vue', // component:'resource/search/index.vue',
// meta: { // meta: {
// title: '库存查询', // title: '库存查询',
// icon: '' // icon: ''
// } // }
// }, // },
// 第二十七个路由对象
{ {
id: 27, id: 27,
pid: 0, pid: 0,
@ -315,6 +348,7 @@ const routes: IRoute[] = [
icon: 'diet' icon: 'diet'
} }
}, },
// 第二十八个路由对象
{ {
id: 28, id: 28,
pid: 27, pid: 27,
@ -326,6 +360,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第二十九个路由对象
{ {
id: 29, id: 29,
pid: 27, pid: 27,
@ -337,6 +372,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第三十个路由对象
{ {
id: 30, id: 30,
pid: 27, pid: 27,
@ -348,6 +384,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第三十一个路由对象
{ {
id: 31, id: 31,
pid: 0, pid: 0,
@ -360,6 +397,7 @@ const routes: IRoute[] = [
icon: 'charge' icon: 'charge'
} }
}, },
// 第三十二个路由对象
{ {
id: 32, id: 32,
pid: 31, pid: 31,
@ -371,10 +409,11 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第三十三个路由对象
{ {
id: 33, id: 33,
pid: 31, pid: 31,
path: 'record', path:'record',
name: 'RecordCharge', name: 'RecordCharge',
component: 'charge/record/index.vue', component: 'charge/record/index.vue',
meta: { meta: {
@ -382,6 +421,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第三十四个路由对象
{ {
id: 34, id: 34,
pid: 31, pid: 31,
@ -393,6 +433,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第三十五个路由对象
{ {
id: 35, id: 35,
pid: 0, pid: 0,
@ -405,10 +446,11 @@ const routes: IRoute[] = [
icon: 'base' icon: 'base'
} }
}, },
// 第三十六个路由对象
{ {
id: 36, id: 36,
pid: 35, pid: 35,
path: 'sale', path:'sale',
name: 'SaleBase', name: 'SaleBase',
redirect: '/base/sale/origin', redirect: '/base/sale/origin',
component: 'base/sale/index.vue', component: 'base/sale/index.vue',
@ -417,6 +459,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第三十七个路由对象
{ {
id: 37, id: 37,
pid: 36, pid: 36,
@ -428,6 +471,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 以下是一些被注释掉的路由对象,可根据需求启用
// { // {
// id: 38, // id: 38,
// pid: 36, // pid: 36,
@ -439,6 +483,7 @@ const routes: IRoute[] = [
// icon: '' // icon: ''
// } // }
// }, // },
// 第三十九个路由对象
{ {
id: 39, id: 39,
pid: 35, pid: 35,
@ -451,6 +496,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第四十个路由对象
{ {
id: 40, id: 40,
pid: 39, pid: 39,
@ -462,6 +508,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 第四十一个路由对象
{ {
id: 41, id: 41,
pid: 39, pid: 39,
@ -473,6 +520,7 @@ const routes: IRoute[] = [
icon: '' icon: ''
} }
}, },
// 以下是一些被注释掉的路由对象,可根据需求启用
// { // {
// id: 42, // id: 42,
// pid: 35, // pid: 35,
@ -483,4 +531,5 @@ const routes: IRoute[] = [
// } // }
] ]
// 导出 routes 数组,使其可以在其他模块中被导入和使用
export default routes export default routes

@ -1,13 +1,20 @@
// 定义一个接口 IUser用于描述用户对象的结构
export interface IUser { export interface IUser {
// 用户的唯一标识 ID类型为数字
id: number id: number
// 用户的用户名,类型为字符串
username: string username: string
// 用户拥有的权限 ID 数组,数组中的元素类型为数字
auth: number[] auth: number[]
} }
// 导出一个默认的用户数组,类型为 IUser 数组
// 数组中包含一个用户对象
export default <IUser[]>[ export default <IUser[]>[
{ {
id: 1, id: 1,
username: 'zhangsan', username: 'zhangsan',
// 该用户拥有的权限 ID 数组,包含多个权限 ID
auth: [ auth: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,

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

Loading…
Cancel
Save