diff --git a/.env.development b/.env.development index 17faa14..f8eaae4 100644 --- a/.env.development +++ b/.env.development @@ -7,4 +7,8 @@ VITE_PUBLIC_PATH = / # 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") VITE_ROUTER_HISTORY = "hash" -VITE_API_BASEURL = "http://localhost:5199/api" +VITE_API_BASEURL = "http://192.168.2.33:5199/api" +# 接口地址 +# VITE_API_BASEURL = "http://localhost:5199/api" +#数据中心后台地址 +VITE_API_USERCENTER_URL = "https://dca.w.23544.com:8843/api" diff --git a/.env.production b/.env.production index 84e6086..55ffd00 100644 --- a/.env.production +++ b/.env.production @@ -10,4 +10,10 @@ VITE_CDN = false # 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) # 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) # 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) -VITE_COMPRESSION = "none" \ No newline at end of file +VITE_COMPRESSION = "none" + + +# 接口地址 +VITE_API_BASEURL = "https://learn-archives-admin.23544.com/api" +#数据中心后台地址 +VITE_API_USERCENTER_URL = "https://dcb.23544.com/api" \ No newline at end of file diff --git a/.env.staging b/.env.staging index 65b57e3..f613c0f 100644 --- a/.env.staging +++ b/.env.staging @@ -8,9 +8,15 @@ VITE_PUBLIC_PATH = / VITE_ROUTER_HISTORY = "hash" # 是否在打包时使用cdn替换本地库 替换 true 不替换 false -VITE_CDN = true +VITE_CDN = false # 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) # 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) # 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) VITE_COMPRESSION = "none" + + +# 接口地址 +VITE_API_BASEURL = "https://learn-archives-admin-dev.23544.com/api" +#数据中心后台地址 +VITE_API_USERCENTER_URL = "https://dca.w.23544.com:8843/api" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 423ed2b..7f9b81f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ tests/**/coverage/ *.ntvs* *.njsproj *.sln -tsconfig.tsbuildinfo \ No newline at end of file +tsconfig.tsbuildinfo +.vscode +.vscode/settings.json diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100644 index 5ee2d16..0000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# shellcheck source=./_/husky.sh -. "$(dirname "$0")/_/husky.sh" - -PATH="/usr/local/bin:$PATH" - -npx --no-install commitlint --edit "$1" \ No newline at end of file diff --git a/.husky/common.sh b/.husky/common.sh deleted file mode 100644 index 5f0540b..0000000 --- a/.husky/common.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -command_exists () { - command -v "$1" >/dev/null 2>&1 -} - -# Workaround for Windows 10, Git Bash and Pnpm -if command_exists winpty && test -t 1; then - exec < /dev/tty -fi diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index 6e229ea..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" -. "$(dirname "$0")/common.sh" - -[ -n "$CI" ] && exit 0 - -PATH="/usr/local/bin:$PATH" - -# Perform lint check on files in the staging area through .lintstagedrc configuration -pnpm exec lint-staged \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 388b96f..975bb66 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,14 @@ { "editor.formatOnType": true, - "editor.formatOnSave": true, + "editor.formatOnSave": false, "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "editor.tabSize": 2, "editor.formatOnPaste": true, "editor.guides.bracketPairs": "active", - "files.autoSave": "afterDelay", + // "files.autoSave": "afterDelay", + "files.autoSave": "off", "git.confirmSync": false, "workbench.startupEditor": "newUntitledFile", "editor.suggestSelection": "first", @@ -40,4 +41,5 @@ "v-ripple" ], "vscodeCustomCodeColor.highlightValueColor": "#b392f0", + "vue3snippets.enable-compile-vue-file-on-did-save-code": true, } \ No newline at end of file diff --git a/Dockerfiles/Dockerfile-staging/Dockerfile b/Dockerfiles/Dockerfile-staging/Dockerfile new file mode 100644 index 0000000..e15a94f --- /dev/null +++ b/Dockerfiles/Dockerfile-staging/Dockerfile @@ -0,0 +1,41 @@ +# 构建阶段 +FROM m.daocloud.io/docker.io/library/node:22.14.0 AS builder + +# 设置工作目录 +WORKDIR /app + +# 设置 npm 镜像源 +RUN npm config set registry https://registry.npmmirror.com/ +RUN npm config set fetch-retries 3 +RUN npm config set fetch-retry-mintimeout 5000 +RUN npm config set fetch-retry-maxtimeout 60000 + +# 安装pnpm +RUN npm install -g pnpm + +# 复制源代码 +COPY . . + +# 设置 pnpm 下载源 +RUN pnpm config set registry https://registry.npmmirror.com/ + +# 安装依赖 +RUN pnpm i --fetch-timeout 300000 + +# 构建项目 +RUN pnpm build:staging + +# 部署阶段 +FROM m.daocloud.io/docker.io/library/nginx:alpine + +# 复制构建产物到 Nginx 目录 +COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html + +# 复制 Nginx 配置 +COPY Dockerfiles/Dockerfile-staging/default.conf /etc/nginx/conf.d/default.conf + +# 暴露端口 +EXPOSE 80 + +# 启动 Nginx +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/Dockerfiles/Dockerfile-staging/default.conf b/Dockerfiles/Dockerfile-staging/default.conf new file mode 100644 index 0000000..3de8a37 --- /dev/null +++ b/Dockerfiles/Dockerfile-staging/default.conf @@ -0,0 +1,47 @@ +server { + listen 80; + server_name localhost; + + # 基础设置 + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + + client_max_body_size 100m; + + # 错误页面配置 + error_page 500 502 503 504 /50x.html; + location = /50x.html { + internal; # 仅用于内部错误请求 + } + + # Gzip 压缩设置 + gzip on; + gzip_min_length 1k; + gzip_comp_level 6; + gzip_types text/plain text/css text/javascript application/json + application/javascript application/x-javascript application/xml; + gzip_vary on; + gzip_disable "MSIE [1-6]\."; + + # 静态资源缓存优化 + location ~* \.(jpg|jpeg|gif|ico|css|js)$ { + expires 7d; + add_header Cache-Control "public, no-transform"; + } + + # API 代理配置 + location /api/ { + proxy_pass http://learn-archives-api-svc:8080/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; # 添加协议头 + + # 优化代理性能 + proxy_connect_timeout 30s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffering on; + } +} \ No newline at end of file diff --git a/index.html b/index.html index 1193eef..770aa19 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,10 @@ + + + + >("get", `public/enum/${type}`); + return http.request>("get", `Public/enum/${type}`); } /** * @description 获取枚举对象 @@ -15,5 +16,5 @@ export function getenum(type) { * @return {object} */ export function getenumDic(type) { - return http.request>("get", `public/enum/${type}/Dic`); + return http.request>("get", `Public/enum/${type}/Dic`); } diff --git a/src/api/exam.ts b/src/api/exam.ts new file mode 100644 index 0000000..0cbf281 --- /dev/null +++ b/src/api/exam.ts @@ -0,0 +1,35 @@ +import { http } from "@/utils/http"; +// import type { Res } from "@/utils/http/types"; + +/** + * @description 导入表单 + * @return {object} + */ +export function ImportExamInfo(id: number, file: File) { + let formData = new FormData(); + formData.append("eId", id.toString()); + formData.append("file", file); + return http.request( + "post", + `ExamClassInfo/Import`, + { + data: formData + }, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + responseType: "blob" + } + ); +} + +/** + * @description 删除班级考试信息 + * @return {object} + */ +export function DeleteExamInfo(data: { classId: number; examId: number }) { + return http.request("post", `ExamClassInfo/DeleteExamInfo`, { + data + }); +} diff --git a/src/api/hTable.ts b/src/api/hTable.ts index 8839c3b..e29d581 100644 --- a/src/api/hTable.ts +++ b/src/api/hTable.ts @@ -1,5 +1,6 @@ import { http } from "@/utils/http"; import type { Res } from "@/utils/http/types"; +import type { ComboModel } from "@/components/hTable/hTable"; export class hTableAPI { url = ""; @@ -10,13 +11,9 @@ export class hTableAPI { PageList(data = {}) { return http.request>("post", `${this.url}/PageList`, { data }); } - Info(id, tag = {}) { - const pUrl = `${this.url}/${id}`; + Info(tag) { + const pUrl = `${this.url}/${tag}`; let getUrl = pUrl; - for (const key in tag) { - const el = tag[key]; - getUrl += (getUrl === pUrl ? "?" : "&") + key + "=" + el; - } return http.request>("get", getUrl); } edit(data) { @@ -26,6 +23,9 @@ export class hTableAPI { return http.request>("post", `${this.url}/Del`, { data }); } querycombo(data) { - return http.request>("post", `${this.url}/QueryCombo`, { data }); + return http.request>("post", `${this.url}/QueryCombo`, { + data + }); } } + diff --git a/src/api/menu.ts b/src/api/menu.ts index dffe752..2250495 100644 --- a/src/api/menu.ts +++ b/src/api/menu.ts @@ -3,7 +3,7 @@ import type { Res } from "@/utils/http/types"; // 定义菜单项接口 export interface MenuItem { - id: number; + id?: number; name: string; path?: string; isButton: boolean; @@ -22,3 +22,28 @@ export interface MenuItem { export function MenuAll() { return http.request>("get", `Menu/All`); } + +/** + * @description 获取所有的菜单 + * @return {object} + */ +export function Edit(info: MenuItem) { + return http.request>("post", `Menu/Edit`, { data: info }); +} +/** + * @description 获取所有的菜单 + * @return {object} + */ +export function Del(ids: number[]) { + return http.request>("post", `Menu/Del`, { data: ids }); +} +/**获取角色的菜单 */ +export function RoleMenu(roleId: number) { + return http.request>("get", `MenuRelation/RoleMenu?roleId=${roleId}`); +} +/**修改角色菜单 */ +export function SetMenu(data: { roleId: number; menuId: number[] }) { + return http.request>("post", `MenuRelation/SetMenu`, { + data: data + }); +} diff --git a/src/api/school.ts b/src/api/school.ts new file mode 100644 index 0000000..8f8e2b6 --- /dev/null +++ b/src/api/school.ts @@ -0,0 +1,30 @@ +import { http } from "@/utils/http"; +import type { Res } from "@/utils/http/types"; + +/** + * @description 获取枚举下拉 + * @param {string} type 枚举类型 type=StatusEnum + * @return {object} + */ +export function getenum(type) { + return http.request>("get", `public/enum/${type}`); +} +/** + * @description 获取枚举对象 + * @param {string} type 枚举类型 type=StatusEnum + * @return {object} + */ +export function getenumDic(type) { + return http.request>("get", `public/enum/${type}/Dic`); +} + +export function getProvince() { + return http.request>("get", `address/province`); +} +export function getcity(c) { + return http.request>("get", `address/${c}/city`); +} +export function getregion(r) { + return http.request>("get", `address/${r}/region`); +} + diff --git a/src/api/student.ts b/src/api/student.ts new file mode 100644 index 0000000..d78e012 --- /dev/null +++ b/src/api/student.ts @@ -0,0 +1,74 @@ +import { http } from "@/utils/http"; +import { Res } from "@/utils/http/types"; +// import type { Res } from "@/utils/http/types"; + +/** + * @description 导入表单 + * @return {object} + */ +export function ImportTeacher(file: File) { + let formData = new FormData(); + formData.append("file", file); + return http.request( + "post", + `Student/ImportTeacher`, + { + data: formData + }, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + responseType: "blob" + } + ); +} + +/** + * @description 导入表单 + * @return {object} + */ +export function ImportStudent(file: File) { + let formData = new FormData(); + formData.append("file", file); + return http.request( + "post", + `Student/Import`, + { + data: formData + }, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + responseType: "blob" + } + ); +} + +/** + * @description 修改学生拓展信息 + * @return {object} + */ +export function EditStudent(data) { + return http.request>("post", `Student/EditInfo`, { + data + }); +} + +/** + * @description PageList + * @return {object} + */ +export function PageList(data) { + return http.request>("post", `Student/PageList`, { + data + }); +} +/** + * @description StudentInfo + * @return {object} + */ +export function StudentInfo(uid) { + return http.request>("get", `Student/Info?uid=${uid}`); +} diff --git a/src/api/toschoolinfomanage.ts b/src/api/toschoolinfomanage.ts new file mode 100644 index 0000000..c48da1f --- /dev/null +++ b/src/api/toschoolinfomanage.ts @@ -0,0 +1,79 @@ +import { http } from "@/utils/http"; +import type { Res } from "@/utils/http/types"; + +/** + * @description 获取学校枚举下拉 + * @param {string} type 枚举类型 type=StatusEnum + * @return {object} + */ +export function getenumApi(data) { + return http.request>("post", `/SchoolBusiness/QueryCombo`, { + data + }); +} +/** + * @description 获取学校枚举下拉 + * @param {string} type 枚举类型 type=StatusEnum + * @return {object} + */ +export function getPageListApi(data) { + return http.request>("post", `/SchoolBusiness/QueryPageList`, { + data + }); +} +/** + * @description 新增赴校信息或者编辑 id:0(新增),其他(编辑) + * @return {object} + */ +export function addOrEditApi(data: any) { + return http.request>("post", `/SchoolBusiness/Edit`, { + data + }); +} +/** + * @description 获取赴校信息详情 + * @param {string} type 枚举类型 type=StatusEnum + * @return {object} + */ +export function getSchoolBusinessDetailApi(id: string | number) { + return http.request>("get", `/SchoolBusiness/${id}`); +} +/** + * @description 删除赴校信息 + * @return {object} + */ +export function deleteSchoolBusinessApi(data: Array) { + return http.request>("post", `/SchoolBusiness/Del`, { + data + }); +} +/** + * @description 获取赴校人员下拉数据 + * @return {object} + */ +export function getSchoolBusinessPeopleListApi(data: object) { + return http.request>("post", `/Admin/QueryCombo`, { + data + }); +} +/** + * @description 导入excel + * @return {object} + */ +export function importExcel(file: File) { + let formData = new FormData(); + formData.append("file", file); + return http.request( + "post", + `SchoolBusiness/Import`, + { + data: formData + }, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + responseType: "blob" + } + ); +} \ No newline at end of file diff --git a/src/api/userCenter.ts b/src/api/userCenter.ts new file mode 100644 index 0000000..bfc6b15 --- /dev/null +++ b/src/api/userCenter.ts @@ -0,0 +1,201 @@ +import { ComboModel } from "@/components/hTable/hTable"; +import { http } from "@/utils/http"; +import type { Res, ResPage } from "@/utils/http/types"; +/** + * @description 获取枚举下拉 + * @param {string} type 枚举类型 type=StatusEnum + * @return {object} + */ +export function getPageUserList(data: any) { + return http.request>( + "post", + `userCenter/back/users/getpageuserlist`, + { + data + } + ); +} +/** + * @description 编辑学校 + * @return {void} + */ +export function EditSchool(data: any) { + return http.request>("post", `userCenter/back/schools/add`, { + data + }); +} +/** + * @description 获取学校 + * @return {void} + */ +export function getSchoolData() { + return http.request>( + "get", + `userCenter/public/getschooldata` + ); +} +/** + * @description 获取职位列表 + * @return {void} + */ +export function getPositionList(data: any) { + return http.request>( + "post", + `userCenter/back/positions/getpositionlist`, + { data } + ); +} +/** + * @description 云校下拉框数据 + * @return {object} + */ +export function cloudSchoolCombo() { + let data = { + ValueName: "Id", + TextName: "Name" + }; + return http.request>( + "post", + `userCenter/back/cloudschool/querycombo`, + { + data + } + ); +} +/** + * @description 获取班级信息 + * @return {object} + */ +export function getClassInfo(id) { + return http.request>("get", `userCenter/back/classes/${id}`); +} +/** + * @description 获取学校信息 + * @return {object} + */ +export function getSchoolInfo(id) { + return http.request>("get", `userCenter/back/schools/${id}`); +} + +/** + * @description 获取用户信息 + * @return {object} + */ +export function getUserInfo(id) { + return http.request>( + "get", + `userCenter/back/users/getuserinfo?id=${id}` + ); +} + +/** + * @description 添加班级 + * @return {object} + */ +export function addClasses(info: any) { + return http.request>("post", `userCenter/back/classes/addclass`, { + data: info + }); +} + +/** + * @description 添加班级 + * @return {object} + */ +export function editUser(data: any) { + return http.request>("post", `userCenter/back/users/edituser`, { + data + }); +} + +/** + * @description 添加班级 + * @return {object} + */ +export function getClassCombo(info: any) { + return http.request>("post", `userCenter/public/getclasscombo`, { + data: info + }); +} + +/** + * @description 获取科目 + * @return {object} + */ +export function getSubjectData() { + return http.request>("get", `userCenter/public/getsubjectdata`); +} +/** + * @description 获取职位列表 + * @return {object} + */ +export function getPositions(data) { + return http.request>("post", `userCenter/back/positions/positions`, { + data + }); +} + +//--------------------------interface---------------------------- + +/** + * 职位信息 + */ +export interface Position { + userId: number; + id: number; + name: string; + schoolId: number; + enable: boolean; + endTime: string | null; + schoolName: string; + graduationYear: number; + grade: string; + classId: number; + className: string; + subjectId: number; + subjectName: string; + positionType: number; + positionLevel: number; + status: boolean; +} + +/** + * 用户详细信息 + */ +export interface UserDetail { + id: number; + templateId: number; + phone: string; + edited: boolean; + userType: number; + cloudSchoolId: number; + account: string; + studentId: string; + realName: string; + sex: number; + subjectLevel: any; + birthDate: string; + residence: string; + national: string; + headImage: string; + idCard: string | null; + pid: number; + pname: string; + cid: number; + cname: string; + rid: number; + rname: string; + wx: string; + isPerfectInfo: number; + level: number; + state: number; + meetingAccount: string | null; + gkSubject: string | null; + glSubject: string | null; + gSubject1: string | null; + gSubject2: string | null; + thirdPartyId: string | null; + pointPenSN: string | null; + subjectLevels: null; + positions: Position[]; +} diff --git a/src/components/hTable/hTable.ts b/src/components/hTable/hTable.ts index eb1a905..c650c2b 100644 --- a/src/components/hTable/hTable.ts +++ b/src/components/hTable/hTable.ts @@ -1,68 +1,98 @@ export interface Dialog { - /* 对话框是否可见 */ + /** 对话框是否可见 */ visible: boolean; - /* 是否显示关闭按钮 */ + /** 是否显示关闭按钮 */ close: boolean; - /* 对话框标题 */ + /** 对话框标题 */ title: string; - /* 对话框宽度 */ + /** 对话框宽度 */ width: string; /**自定义弹窗数据 */ custom: { - /* 自定义对话框高度 */ + /** 自定义对话框高度 */ height: string; - /* 自定义对话框数据 */ + /** 自定义对话框数据 */ data: any[]; - /* 自定义组件路径 */ + /** 自定义组件路径 */ src?: string; - /* 自定义配置项 */ + /** 自定义配置项 */ custom: Record; - /* 自定义对话框是否可见 */ + /** 自定义对话框是否可见 */ visible: boolean; - /* 异步加载组件 */ + /** 异步加载组件 */ component: any; }; edit: { - /* 编辑项ID */ + /** 编辑项ID */ id: number; - /* 编辑对话框标题 */ + /** 编辑对话框标题 */ title: string; - /* 编辑对话框是否可见 */ + /** 编辑对话框是否可见 */ visible: boolean; row?: any; tagData?: any; }; } -/* 按钮自定义配置 */ +/** 按钮自定义配置 */ export interface ButtonCustomConfig { - /* 弹出框标题 */ + /** 弹出框标题 */ title: string; - /* 组件路径 */ + /** 组件路径 */ src: string; - /* 弹框宽度 */ + /** 弹框宽度 */ width: string; - /* 弹框高度 */ + /** 弹框高度 */ height: string; } -/* 操作按钮配置 */ +/** 操作按钮配置 */ export interface OperationButton { - /* 是否为头部按钮 */ + /** 是否为头部按钮 */ topBtn: boolean; - /* 是否显示 */ + /** 按钮权限码 */ + perms?: string; + /** 是否显示 */ show?: boolean; - /* 按钮文本 */ + /** 按钮文本 */ label: string; - /* 按钮类型 */ - btnType: "add" | "edit" | "del" | "custom"; - /* 按钮样式 */ - btnStyle?: "success" | "danger"; - /* 自定义按钮配置 */ + /** 按钮点击事件 + * @tips btnType 为空时触发 + * @param obj 当前按钮配置对象 + * @param row 当前行数据 + * @param handleReloadPaged 父表单刷新函数 + */ + click?: (obj, row, handleReloadPaged: (reload?: boolean) => void) => void; + /** 按钮类型 */ + btnType?: "add" | "edit" | "del" | "custom"; + /** 按钮样式 */ + btnStyle?: "success" | "info" | "primary" | "danger" | "warning"; + /** 自定义按钮配置 */ custom?: ButtonCustomConfig; } -/* 字段设置项 */ +/** 类型判断枚举 */ +export enum ConditionalType { + Equal, + Like, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + In, + NotIn, + LikeLeft, + LikeRight, + NoEqual, + IsNullOrEmpty, + IsNot, + NoLike, + EqualNull, + InLike, + Range +} + +/** 字段设置项 */ export interface FieldSetting { /**map 时Value的取值的属性 */ mapValue?: string; @@ -77,50 +107,45 @@ export interface FieldSetting { * @returns 预期返回有效图片地址url */ imgUrl?: (value: any, row: any) => string; - /* 数据源 */ - datasource?: Array<{ - Value: any; - Text: string; - }>; + /** 数据源 */ + datasource?: ComboModel[]; } -///* 表格列配置 */ +///** 表格列配置 */ //export interface TableEditColumn {} - -/* 表格列配置 */ +export interface ComboModel { + value: any; + text: string; +} +/** 表格列配置 */ export interface TableColumn { - /* 显示标签 */ + /** 显示标签 */ label: string; - /* 是否可搜索 */ + /** 是否可搜索 */ search: boolean; - /* 搜索类型 */ - searchType?: - | "Equal" - | "NoEqual" - | "Like" - | "GreaterThan" - | "LessThan" - | "NoLike"; - /* 是否允许添加 */ - add: boolean; - /* 是否允许修改 */ - edit: boolean; - /* 列宽度 */ + /** 搜索类型 */ + searchType?: ConditionalType; + /** 是否允许添加 [false]*/ + add?: boolean; + /** 是否允许修改 [false]*/ + edit?: boolean; + /** 列宽度 [auto]*/ width?: string; - /* 字段类型 */ - type?: string; + /** 字段类型 */ + type?: "string" | "dropdown" | "switch" | "img" | "datetime" | "textarea"; /** 是否多选 */ multiple?: boolean; /** 编辑时显示列 */ editShow?: boolean; + /**校验规则 */ rules?: any | Array; /** 显示列 */ show?: boolean; - /* 字段设置 */ + /** 字段设置 */ setting?: FieldSetting; - /* 修改时的编辑值 */ - valueE?: Array | string; - /* 查询值 */ - value?: Array | string; + /** 修改时的编辑值 */ + valueE?: Array | string | number | boolean | Date; + /** 查询值 */ + value?: Array | string | number | boolean | Date; /** textarea编辑时的行数 */ editRows?: number; /**编辑时值发生变化 */ @@ -129,54 +154,73 @@ export interface TableColumn { custom?: (row: any) => string; } -/* 分页数据 */ +/** 分页数据 */ export interface PageData { - /* 总条数 */ + /** 总条数 */ total: number; } -/* 搜索条件 */ +/** 分页数据 */ +export interface ConditionalModel { + /** 字段名称 */ + FieldName: string; + /** 字段查询值 */ + FieldValue: string; + /** 查询方式 */ + ConditionalType?: ConditionalType; + /** C#类型名称 */ + CSharpTypeName?: string; +} + +/** 搜索条件 */ export interface SearchConditions { - /* 是否显示搜索 */ + /** 是否显示搜索 */ show: boolean; - /* 当前页码 */ + /** 当前页码 */ PageIndex: number; - /* 每页大小 */ + /** 每页大小 */ PageSize: number; - /* 排序字段 */ + /** 排序字段 */ OrderBy: string; - /* 默认查询条件 */ - defaultConditions: any[]; - /* 查询条件 */ + /**排序顺序 + * @tips 0:升序 1:降序 + * @默认 = 1 + */ + OrderByType?: 0 | 1; + /** 默认查询条件 */ + defaultConditions: ConditionalModel[]; + /** 查询条件 */ Conditions: any[]; } -/* 表格配置 */ +/** 表格配置 */ export interface TableConfig { - /* 搜索回调函数 */ + /** 搜索回调函数 */ searchCallback?: (s: SearchConditions) => void; - /* 新增/修改回调函数 */ + /** 新增/修改回调函数 */ editCallback?: (from: any) => void; - /* API地址 */ + /** API地址 */ apiUrl: string; - /* 是否显示选择列 */ + /** 是否显示选择列 */ selectColumn: boolean; - /* 搜索配置 */ + /** 搜索配置 */ search: SearchConditions; - /* 是否显示操作列 */ + /** 是否显示操作列 */ operationColumn: boolean; - /* 操作按钮配置 */ + /** 操作按钮配置 */ operationColumnData: OperationButton[]; - /* 列配置 */ + /** 列配置 */ column: Record; - /* 表格数据 */ + /** 表格数据 */ data: any[]; /**显示头部操作按钮 */ operationTop?: boolean; - /* 分页数据 */ + /** 分页数据 */ pageData: PageData; - /* 选中行 */ + /** 选中行 */ selectRows: any[]; - /* 是否显示边框 */ + /** 是否显示边框 */ border: boolean; + /**是否显示 */ + show?: boolean; } diff --git a/src/components/hTable/hTableEdit.vue b/src/components/hTable/hTableEdit.vue index f35ec13..3e84f98 100644 --- a/src/components/hTable/hTableEdit.vue +++ b/src/components/hTable/hTableEdit.vue @@ -8,24 +8,24 @@ const props = defineProps({ //** 传入的表单数据 */ id: { type: Number, - default: -1 + default: -1, }, tableData: { type: Object as PropType, - default: null + default: null, }, row: { type: Object, - default: null + default: null, }, tagData: { type: Object, - default: () => {} - } + default: () => {}, + }, }); const emit = defineEmits(["handlePagedCallback"]); const editFormRef = ref(); -const column: Record = {}; +const column = ref>({}); const editData = ref({ frorm: {}, isedit: props.id !== -1, @@ -34,12 +34,12 @@ const editData = ref({ { required: true, message: "不能为空", - trigger: "blur" - } + trigger: "blur", + }, ], formLabelWidth: "120px", size: "small", - loading: false + loading: false, }); const Api = new hTableAPI(editData.value.table.apiUrl); onMounted(() => { @@ -69,16 +69,16 @@ function handlePagedCallback() { emit("handlePagedCallback"); // 传参给父组件 } function handleSubmitForm() { - editFormRef.value.validate(valid => { + editFormRef.value.validate((valid) => { if (!valid) { return; } editData.value.loading = true; - let form = {}; + let form: any = {}; if (editData.value.isedit) { form = props.row; - } + } else form.id = 0; for (const key in column.value) { const element = column.value[key]; if (element.valueE !== null && element.valueE !== "") { @@ -88,7 +88,7 @@ function handleSubmitForm() { if (editData.value.table.editCallback) { editData.value.table.editCallback(form); } - Api.edit(form).then(res => { + Api.edit(form).then((res) => { editData.value.loading = false; if (res.code === 200) { ElMessage.success("操作成功"); @@ -105,7 +105,7 @@ function handleResetForm() { if (Array.isArray(item.valueE)) { item.valueE = []; } else if (typeof item.valueE === "number") { - item.valueE = 0; + item.valueE = ""; } else if (typeof item.valueE === "boolean") { item.valueE = false; } else { @@ -116,9 +116,9 @@ function handleResetForm() { function fetchInitData() {} function fetchFormData() { editData.value.loading = false; - handleResetForm(); if (editData.value.isedit) { - Api.Info(props.id).then(res => { + handleResetForm(); + Api.Info(props.id).then((res) => { if (res.code === 200) { editData.value.frorm = res.data; for (const key in column.value) { @@ -141,7 +141,6 @@ function fetchFormData() { ref="editFormRef" :model="editData.table.column" :label-width="editData.formLabelWidth" - size="small" clearable >
@@ -170,12 +169,12 @@ function fetchFormData() { clearable filterable :placeholder="o.label" - style="width: 100%" + class="elWidth" @change="o.change" >
- +
- 立即提交 重置 @@ -216,3 +214,9 @@ function fetchFormData() { + diff --git a/src/components/hTable/index.vue b/src/components/hTable/index.vue index ba33a09..e832ba8 100644 --- a/src/components/hTable/index.vue +++ b/src/components/hTable/index.vue @@ -10,33 +10,49 @@ import { onUnmounted, getCurrentInstance, onBeforeMount, - PropType + PropType, + shallowRef, } from "vue"; import { Search } from "@element-plus/icons-vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { defineAsyncComponent, AsyncComponentLoader } from "vue"; -import { Dialog, TableConfig } from "./hTable"; +import { + ButtonCustomConfig, + ConditionalType, + Dialog, + TableColumn, + TableConfig, +} from "./hTable"; import hTableEdit from "./hTableEdit.vue"; import { hTableAPI } from "@/api/hTable"; import { getenum } from "@/api/enum"; +//按钮权限 +import { hasPerms } from "@/utils/auth"; +const modules = import.meta.glob("/src/views/**/*.{vue,tsx}"); const props = defineProps({ //** 传入的表单数据 */ Row: { type: Object as PropType, - default: () => ({}) + default: () => ({}), }, tableConfig: { type: Object as PropType, - default: () => ({}) - } + default: () => ({}), + }, }); const table = ref(props.tableConfig); +const tableShowColumn = ref>(); onBeforeMount(() => { /* 初始化系统配置 */ nextTick(async () => { intdata(); + //过滤无法显示的列 + tableShowColumn.value = Object.fromEntries( + Object.entries(table.value.column).filter(([_, s]) => s.show) + ); + Api = new hTableAPI(table.value.apiUrl); init.value = true; tableShow.value = true; @@ -55,7 +71,6 @@ onUnmounted(() => {}); // }); const tableShow = ref(false); let Api: hTableAPI = null; -const instance = getCurrentInstance(); const init = ref(false); const tableHeight = ref(0); @@ -70,13 +85,13 @@ const dialog = ref({ data: [], custom: {}, component: null, //自定义弹窗的路径 - visible: false // 弹出层是否显示 + visible: false, // 弹出层是否显示 }, edit: { id: -1, title: "", // 弹出层title - visible: false // 弹出层是否显示 - } + visible: false, // 弹出层是否显示 + }, }); const appB = ref(null); @@ -86,7 +101,8 @@ function appStyle() { tableHeight.value = appB.value.parentElement.parentElement.offsetHeight - 145 - - appB_S.value.offsetHeight; + appB_S.value.offsetHeight + + 0; return tableHeight; } @@ -100,18 +116,18 @@ function intdata() { // 处理 column 的属性 for (const key in table.value.column) { const element = table.value.column[key]; - + if (element.add === undefined) element.add = false; + if (element.edit === undefined) element.edit = false; // Vue 3 中直接赋值即可,不需要 $set - if (element.valueE === undefined) - element.valueE = element.multiple ? [] : ""; + if (element.valueE === undefined) element.valueE = element.multiple ? [] : ""; if (element.value === undefined) element.value = element.multiple ? [] : ""; if (!element.type) element.type = "string"; if (element.show === undefined) element.show = true; if (element.editShow === undefined) element.editShow = true; if (!element.setting) - element.setting = { datasource: [], mapValue: "Value", maplabel: "Text" }; - if (!element.setting.mapValue) element.setting.mapValue = "Value"; - if (!element.setting.maplabel) element.setting.maplabel = "Text"; + element.setting = { datasource: [], mapValue: "value", maplabel: "text" }; + if (!element.setting.mapValue) element.setting.mapValue = "value"; + if (!element.setting.maplabel) element.setting.maplabel = "text"; if (!element.change) element.change = () => {}; } @@ -127,21 +143,21 @@ function rowKeyFun(row) { return row.customId; } function execute(obj, scope, btn) { - if (Object.prototype.toString.call(obj) === "[object Function]") - return obj(scope, btn); + if (Object.prototype.toString.call(obj) === "[object Function]") return obj(scope, btn); return eval(obj); } function getOperationColumnWidth() { - let hFontSize = getComputedStyle(window.document.documentElement)[ - "font-size" - ].replace("px", ""); + let hFontSize = getComputedStyle(window.document.documentElement)["font-size"].replace( + "px", + "" + ); let defWidth = 10 + 2 + 30; let width = eval( table.value.operationColumnData - .filter(s => !s.topBtn && s.show) - .map(s => defWidth + s.label.length * (hFontSize || 16)) + .filter((s) => !s.topBtn && s.show) + .map((s) => defWidth + s.label.length * (hFontSize || 16)) .join("+") ); width = width < 100 ? 100 : width; @@ -169,9 +185,12 @@ function handleAdd() { dialog.value.edit.visible = true; dialog.value.visible = true; dialog.value.width = "500px"; + + dialog.value.edit.row = null; + dialog.value.edit.tagData = null; } function handleEdit(obj, row) { - dialog.value.edit.id = row[0].Id; + dialog.value.edit.id = row[0].id; dialog.value.edit.row = row[0]; dialog.value.edit.tagData = obj.tagData; dialog.value.title = obj.label || "修改"; @@ -180,13 +199,18 @@ function handleEdit(obj, row) { dialog.value.visible = true; dialog.value.width = "500px"; } -function handleCustom(obj, row, custom) { +async function handleCustom(obj, row, custom: ButtonCustomConfig) { dialog.value.custom.data = row || []; dialog.value.custom.custom = custom || []; // 异步加载组件 - dialog.value.custom.component = defineAsyncComponent({ - loader: () => import(/* @vite-ignore */ `../${custom.src}.vue`) - }); + // dialog.value.custom.component = defineAsyncComponent({ + // loader: () => import(/* @vite-ignore */ `../../views/${custom.src}.vue`), + // }); + let r = shallowRef(null); + const module: any = await modules[`/src/views/${custom.src}.vue`](); //import(`@/views/${custom.src}.vue`); + r.value = module.default; + dialog.value.custom.component = r; + dialog.value.width = custom.width; dialog.value.title = custom.title; dialog.value.edit.visible = false; @@ -205,11 +229,11 @@ function handleDelete(obj, row) { return; } const ids: any[] = []; - row.forEach(it => { - ids.push(it.Id); + row.forEach((it) => { + ids.push(it.id); }); ElMessageBox.confirm("此操作将永久删除勾选记录, 是否继续?").then(() => { - Api.delete(ids).then(res => { + Api.delete(ids).then((res) => { if (res.code === 200) { handleReloadPaged(); ElMessage.success("删除成功"); @@ -225,8 +249,23 @@ function handleAddCallback() { dialog.value.visible = false; dialog.value.edit.visible = false; dialog.value.custom.visible = false; + + handleResetForm(); handleReloadPaged(); } +function handleResetForm() { + for (const key in table.value.column) { + let item = table.value.column[key]; + if (Array.isArray(item.valueE)) { + item.valueE = []; + } else if (typeof item.valueE === "number") { + item.valueE = ""; + } else if (typeof item.valueE === "boolean") { + } else { + item.valueE = ""; + } + } +} function tableClose() { handleAddCallback(); } @@ -244,12 +283,22 @@ function handleSelectionChange(selection) { table.value.selectRows = selection; } +function searchReload() { + for (let name in table.value.column) { + if ( + !table.value.column[name].search || + table.value.column[name].value === undefined || + table.value.column[name].value === null || + table.value.column[name].value === "" + ) { + continue; + } + table.value.column[name].value = ""; + } +} // 查询 function handleReloadPaged(reload = true) { - if ( - table.value.search === undefined || - table.value.search.PageIndex === undefined - ) { + if (table.value.search === undefined || table.value.search.PageIndex === undefined) { table.value.search.PageIndex = 0; table.value.search.PageSize = 20; } @@ -266,17 +315,19 @@ function handleReloadPaged(reload = true) { let data: any = { ConditionalType: 0 }; if (table.value.column[name].type === "datetime") { // data.CSharpTypeName = 'DateTime' - data.ConditionalType = 8; // '2023-10-07%' + data.ConditionalType = ConditionalType.LikeLeft; // '2023-10-07%' } else if (table.value.column[name].type === "switch") { data.CSharpTypeName = "Boolean"; } else if (table.value.column[name].type === "string") { - data.ConditionalType = "Like"; - } + data.ConditionalType = ConditionalType.Like; + } else data.ConditionalType = ConditionalType.Equal; + data.FieldName = name.charAt(0).toUpperCase() + name.slice(1); - data.FieldValue = table.value.column[name].value; + data.FieldValue = table.value.column[name].value.toString(); if (table.value.column[name].searchType != undefined) { - data.ConditionalType = table.value.column[name].searchType || 0; + let v: number = table.value.column[name].searchType || 0; + data.ConditionalType = v; } table.value.search.Conditions.push(data); } @@ -295,11 +346,10 @@ async function fetchInitData() { const element = table.value.column[key]; if (element.type === "dropdown") { if (!element.setting.datasource) { - // 存在值就不处理 - - let rdata = await eval(element.setting.datasourceStr); - element.setting.datasource = rdata.data; - console.log(key + " " + element.setting.datasourceStr, rdata); + // 不安全取消 存在值就不处理 + // let rdata = await eval(element.setting.datasourceStr); + // element.setting.datasource = rdata.data; + // console.log(key + " " + element.setting.datasourceStr, rdata); } } if ( @@ -309,14 +359,18 @@ async function fetchInitData() { element.type === "string" || element.type === undefined) ) { - element.custom = row => { - let sc = element.setting.datasource.find( - s => s[element.setting.mapValue] + "" == row[key] + "" - ); - return !sc ? row[key] : sc[element.setting.maplabel]; - }; + if (element.type === "string" || element.type === undefined) + element.custom = (row) => row[key]; + else { + element.custom = (row) => { + let sc = element.setting.datasource.find( + (s) => s[element.setting.mapValue] + "" == row[key] + "" + ); + return !sc ? row[key] : sc[element.setting.maplabel]; + }; + } } else if (element.custom == undefined) { - element.custom = row => row[key]; + element.custom = (row) => row[key]; } } setTimeout(() => { @@ -328,9 +382,7 @@ function showTips(item, value) { if (item.width == undefined) { return false; } - return ( - getByteLen(item.custom(value) + "") * 16 < item.width.replace("px", "") - ); + return getByteLen(item.custom(value) + "") * 16 < item.width.replace("px", ""); } function getByteLen(str) { let len = 0; @@ -343,11 +395,11 @@ function getByteLen(str) { function fetchPagedData() { for (const iterator of table.value.search.defaultConditions) { if (!iterator) continue; - if (!table.value.search.Conditions.find(s => s == iterator)) { + if (!table.value.search.Conditions.find((s) => s == iterator)) { table.value.search.Conditions.push(iterator); } } - Api.PageList(table.value.search).then(res => { + Api.PageList(table.value.search).then((res) => { if (res.code === 200) { table.value.data = res.data.data.map((s, i) => { return { ...s, customId: i }; @@ -363,17 +415,13 @@ function fetchPagedData() {
- + @@ -388,7 +436,7 @@ function fetchPagedData() { > - 查询 + + 重置 + +
+ + + + +
{{ e.label }} + >{{ e.label }}
{{ item.custom(scope.row) }}
- -
-
+
-
- - - /> - - -
@@ -571,26 +620,28 @@ function fetchPagedData() { .columnTemplate .btn { margin-left: 0 !important; } + .columnTemplate { display: flex; + flex-direction: row; + flex-flow: row wrap; gap: 5px; + place-content: flex-start flex-start; + align-items: center; -webkit-box-orient: horizontal; -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - flex-wrap: wrap; - align-items: center; - justify-content: flex-start; - align-content: flex-start; } + .sty .tab_tip { - white-space: nowrap; - text-overflow: ellipsis; overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + .toolbar-container { padding-bottom: 5px; } + .maxWidth600px { max-width: 600px; } diff --git a/src/layout/components/lay-navbar/index.vue b/src/layout/components/lay-navbar/index.vue index 07cb985..787c5b1 100644 --- a/src/layout/components/lay-navbar/index.vue +++ b/src/layout/components/lay-navbar/index.vue @@ -19,7 +19,7 @@ const { userName, userAvatar, avatarsStyle, - toggleSideBar + toggleSideBar, } = useNav(); @@ -45,7 +45,7 @@ const { - + @@ -55,20 +55,13 @@ const { - + diff --git a/src/layout/components/lay-notice/index.vue b/src/layout/components/lay-notice/index.vue index 55baa69..d250e20 100644 --- a/src/layout/components/lay-notice/index.vue +++ b/src/layout/components/lay-notice/index.vue @@ -8,11 +8,10 @@ const noticesNum = ref(0); const notices = ref(noticesData); const activeKey = ref(noticesData[0]?.key); -notices.value.map(v => (noticesNum.value += v.list.length)); +notices.value.map((v) => (noticesNum.value += v.list.length)); -const getLabel = computed( - () => item => - item.name + (item.list.length > 0 ? `(${item.list.length})` : "") +const getLabel = computed(() => (item) => + item.name + (item.list.length > 0 ? `(${item.list.length})` : "") ); @@ -23,7 +22,7 @@ const getLabel = computed( 'dropdown-badge', 'navbar-bg-hover', 'select-none', - Number(noticesNum) !== 0 && 'mr-[10px]' + Number(noticesNum) !== 0 && 'mr-[10px]', ]" > @@ -40,11 +39,7 @@ const getLabel = computed( class="dropdown-tabs" :style="{ width: notices.length === 0 ? '200px' : '330px' }" > - +