瀏覽代碼

政策分享

zhuangyunsheng 1 年之前
父節點
當前提交
02766cfe0d

+ 3 - 3
.env

@@ -1,10 +1,10 @@
 # 所有环境都会加载
 
 # 项目标识代码
-VITE_APP_CODE="TJM_VUE3_ELEPLUS_ADMIN"
+VITE_APP_CODE = "TJM_VUE3_ELEPLUS_ADMIN"
 
 # 项目名
-VITE_APP_NAME="玉衡平台"
+VITE_APP_NAME = "玉衡平台"
 
 # 项目描述
-VITE_APP_DESCRIPTION="玉衡框架vue3版本,提供基础服务"
+VITE_APP_DESCRIPTION = "玉衡框架vue3版本,提供基础服务"

+ 15 - 12
.env.development

@@ -6,28 +6,31 @@ VITE_APP_ENV = "development"
 # 公共基础路径
 VITE_BASE = "/"
 # 代理URL路径
-VITE_BASE_URL ="/dev-api"
+VITE_BASE_URL = "/dev-api"
 # 模拟数据接口路径
-VITE_BASE_MOCK_URL ="/mock-api"
+VITE_BASE_MOCK_URL = "/mock-api"
 
 # boot服务端接口路径
-VITE_BASE_SERVER_URL ="http://192.168.101.93:8000"
+# VITE_BASE_SERVER_URL = "http://192.168.101.93:8000"
+# VITE_BASE_SERVER_URL = "http://10.206.20.153:8000" # 青岛港-内网
+# VITE_BASE_SERVER_URL = "http://192.168.43.231:8000" # 热点
+VITE_BASE_SERVER_URL = "http://192.168.1.133:8000" # 青岛港-7楼
 # 微前端-工作流
-VITE_WORKFLOW_URL ="http://localhost:1888/"
+VITE_WORKFLOW_URL = "http://localhost:1888/"
 # 微前端-代码生成器
 VITE_CODEMAKER_URL = "http://localhost:32582/"
 
 # 宝信单点登录
-VITE_BX_CLIENT_ID = '项目英文名'
-VITE_BX_AUTH_URL = 'http://eplattest.qdgwlds.com/cloud/oauth/authorize'
-VITE_BX_REDIRECT_URL = 'http://localhost:8080/yh'
-VITE_BX_LOGOUT_URL = 'http://eplattest.qdgwlds.com/cloud/'
+VITE_BX_CLIENT_ID = "项目英文名"
+VITE_BX_AUTH_URL = "http://eplattest.qdgwlds.com/cloud/oauth/authorize"
+VITE_BX_REDIRECT_URL = "http://localhost:8080/yh"
+VITE_BX_LOGOUT_URL = "http://eplattest.qdgwlds.com/cloud/"
 
 # 边端单点登录
-VITE_EDGE_CLIENT_ID ='frame-demo'
-VITE_EDGE_AUTH_URL = 'http://qdport.auth.vue3.10.236.3.36.nip.io/oauth/authorize'
-VITE_EDGE_REDIRECT_URL = 'http://localhost:8080/ssoyh'
-VITE_EDGE_LOGOUT_URL = 'http://qdport.auth.vue3.10.236.3.36.nip.io/logout'
+VITE_EDGE_CLIENT_ID = "frame-demo"
+VITE_EDGE_AUTH_URL = "http://qdport.auth.vue3.10.236.3.36.nip.io/oauth/authorize"
+VITE_EDGE_REDIRECT_URL = "http://localhost:8080/ssoyh"
+VITE_EDGE_LOGOUT_URL = "http://qdport.auth.vue3.10.236.3.36.nip.io/logout"
 
 # 打包是否使用Mock
 VITE_APP_PRODMOCK = false

+ 1 - 0
.eslintrc-auto-import.json

@@ -302,6 +302,7 @@
     "whenever": true,
     "ElMessage": true,
     "ElMessageBox": true,
+    "ElNotification": true,
     "useClipboardItems": true,
     "DirectiveBinding": true,
     "MaybeRef": true,

+ 8 - 8
.eslintrc.cjs

@@ -1,5 +1,5 @@
 
-require('@rushstack/eslint-patch/modern-module-resolution')
+require("@rushstack/eslint-patch/modern-module-resolution")
 
 module.exports = {
     env: {
@@ -8,18 +8,18 @@ module.exports = {
         es2021: true,
     },
     root: true,
-    'extends': [
-        './.eslintrc-auto-import.json',
-        'plugin:vue/vue3-essential',
-        'eslint:recommended',
-        '@vue/eslint-config-prettier/skip-formatting'
+    "extends": [
+        "./.eslintrc-auto-import.json",
+        "plugin:vue/vue3-essential",
+        "eslint:recommended",
+        "@vue/eslint-config-prettier/skip-formatting"
     ],
     parserOptions: {
-        ecmaVersion: 'latest'
+        ecmaVersion: "latest"
     },
     rules: {
         semi: ["warn", "never"],
         "no-debugger": "warn",
-        'vue/multi-word-component-names': 'off'
+        "vue/multi-word-component-names": "off"
     }
 }

+ 1 - 1
.gitignore

@@ -27,4 +27,4 @@ package-lock.json
 *.ntvs*
 *.njsproj
 *.sln
-*.sw?
+*.sw?

+ 7 - 7
.prettierrc.json

@@ -1,9 +1,9 @@
 {
-  "$schema": "https://json.schemastore.org/prettierrc",
-  "semi": false,
-  "singleQuote": true,
-  "printWidth": 80,
-  "trailingComma": "none",
-  "arrowParens": "avoid",
-  "tabWidth": 2
+    "$schema": "https://json.schemastore.org/prettierrc",
+    "semi": false,
+    "singleQuote": true,
+    "printWidth": 80,
+    "trailingComma": "none",
+    "arrowParens": "avoid",
+    "tabWidth": 2
 }

+ 44 - 44
package.json

@@ -1,46 +1,46 @@
 {
-  "name": "tjm-vue3-elplus-admin",
-  "version": "0.0.0",
-  "private": true,
-  "scripts": {
-    "dev": "vite --mode development",
-    "build": "vite build --mode development",
-    "preview": "vite preview",
-    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
-    "format": "prettier --write src/"
-  },
-  "dependencies": {
-    "@element-plus/icons-vue": "^2.1.0",
-    "@micro-zoe/micro-app": "^1.0.0-rc.4",
-    "@vueuse/core": "^10.5.0",
-    "axios": "^1.5.1",
-    "crypto-js": "^4.2.0",
-		"echarts": "5.4.1",
-		"element-plus": "^2.6.0",
-    "fuse.js": "^6.6.2",
-    "js-cookie": "^3.0.5",
-    "mitt": "^3.0.1",
-    "pinia": "^2.1.6",
-    "pinia-plugin-persist": "^1.0.0",
-    "vue": "^3.3.4",
-    "vue-i18n": "^9.5.0",
-    "vue-router": "^4.2.4"
-  },
-  "devDependencies": {
-    "@iconify-json/ep": "^1.1.12",
-    "@iconify-json/mdi": "^1.1.54",
-    "@rushstack/eslint-patch": "^1.3.3",
-    "@vitejs/plugin-vue": "^4.3.4",
-    "@vue/eslint-config-prettier": "^8.0.0",
-    "eslint": "8.35.0",
-		"eslint-plugin-vue": "9.9.0",
-    "prettier": "^3.0.3",
-    "sass": "1.58.3",
-		"sass-loader": "10.1.1",
-    "unplugin-auto-import": "^0.16.6",
-    "unplugin-icons": "^0.17.0",
-    "unplugin-vue-components": "^0.25.2",
-    "vite": "^4.4.9",
-    "vite-plugin-svg-icons": "^2.0.1"
-  }
+    "name": "tjm-vue3-elplus-admin",
+    "version": "0.0.0",
+    "private": true,
+    "scripts": {
+        "dev": "vite --mode development",
+        "build": "vite build --mode development",
+        "preview": "vite preview",
+        "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
+        "format": "prettier --write src/"
+    },
+    "dependencies": {
+        "@element-plus/icons-vue": "^2.1.0",
+        "@micro-zoe/micro-app": "^1.0.0-rc.4",
+        "@vueuse/core": "^10.5.0",
+        "axios": "^1.5.1",
+        "crypto-js": "^4.2.0",
+        "echarts": "5.4.1",
+        "element-plus": "^2.6.0",
+        "fuse.js": "^6.6.2",
+        "js-cookie": "^3.0.5",
+        "mitt": "^3.0.1",
+        "pinia": "^2.1.6",
+        "pinia-plugin-persist": "^1.0.0",
+        "vue": "^3.3.4",
+        "vue-i18n": "^9.5.0",
+        "vue-router": "^4.2.4"
+    },
+    "devDependencies": {
+        "@iconify-json/ep": "^1.1.12",
+        "@iconify-json/mdi": "^1.1.54",
+        "@rushstack/eslint-patch": "^1.3.3",
+        "@vitejs/plugin-vue": "^4.3.4",
+        "@vue/eslint-config-prettier": "^8.0.0",
+        "eslint": "8.35.0",
+        "eslint-plugin-vue": "9.9.0",
+        "prettier": "^3.0.3",
+        "sass": "1.58.3",
+        "sass-loader": "10.1.1",
+        "unplugin-auto-import": "^0.16.6",
+        "unplugin-icons": "^0.17.0",
+        "unplugin-vue-components": "^0.25.2",
+        "vite": "^4.4.9",
+        "vite-plugin-svg-icons": "^2.0.1"
+    }
 }

+ 51 - 0
src/api/folder.js

@@ -0,0 +1,51 @@
+import axios from "axios"
+import request from "@/utils/request"
+
+// 登录方法
+export default {
+    up: function (data, config = {}) {
+        return request({ // file: file
+            headers: {
+                "Content-Type": "multipart/form-data"
+            },
+            url: "/qdport-resource/oss/endpoint/put-file",
+            method: "post",
+            data,
+            ...config
+        })
+    },
+
+    rm: function (fileName) { // fileName: string
+        request({
+            headers: {
+                "Content-Type": "application/x-www-form-urlencoded"
+            },
+            url: "/qdport-resource/oss/endpoint/remove-file",
+            method: "post",
+            data: {
+                fileName
+            }
+        })
+    },
+
+    fileRemove: function (ids) {
+        return request({
+            headers: {
+                "Content-Type": "application/x-www-form-urlencoded"
+            },
+            url: "/qdport-zcgx/file/remove",
+            method: "post",
+            data: {
+                ids
+            }
+        })
+    },
+
+    download: function (url) { // url: string
+        return axios({
+            url,
+            method: "get",
+            responseType: "blob"
+        })
+    }
+}

+ 85 - 0
src/api/policy/share.js

@@ -0,0 +1,85 @@
+import request from "@/utils/request";
+
+export default {
+    // 获取当前组织/单位
+    getUserDept: function (deptId) {
+        return request({
+            url: `/qdport-zcgx/system/${deptId}`,
+            method: "get"
+        })
+    },
+
+    get: function (params) {
+        return request({
+            url: "/qdport-zcgx/share/page",
+            method: "get",
+            params
+        })
+    },
+
+    detail: function (id) {
+        return request({
+            url: `/qdport-zcgx/share/${id}`,
+            method: "get"
+        })
+    },
+
+    add: function (data) {
+        return request({
+            url: "/qdport-zcgx/share/save",
+            method: "post",
+            data
+        })
+    },
+
+    edit: function (data) {
+        return request({
+            url: "/qdport-zcgx/share/update",
+            method: "post",
+            data
+        })
+    },
+
+    del: function (data) {
+        return request({
+            headers: {
+                "Content-Type": "application/x-www-form-urlencoded"
+            },
+            url: "/qdport-zcgx/share/remove",
+            method: "post",
+            data
+        })
+    },
+
+    saveApprove: function (data) { // 直接上报/提交
+        return request({
+            url: "/qdport-zcgx/share/saveApprove",
+            method: "post",
+            data
+        })
+    },
+
+    withdraw: function (data) { // 撤回
+        return request({
+            url: "/qdport-zcgx/share/withdraw",
+            method: "post",
+            data
+        })
+    },
+
+    agree: function (data) { // 审核通过
+        return request({
+            url: "/qdport-zcgx/share/approve",
+            method: "post",
+            data
+        })
+    },
+
+    // withdraw: function (data) { // 退回
+    //     return request({
+    //         url: "/qdport-zcgx/share/withdraw",
+    //         method: "post",
+    //         data
+    //     })
+    // }
+}

+ 11 - 0
src/api/policy/todo.js

@@ -0,0 +1,11 @@
+import request from "@/utils/request";
+
+export default {
+    get: function (params) {
+        return request({
+            url: "/qdport-zcgx/todo/page",
+            method: "get",
+            params
+        })
+    }
+}

+ 0 - 11
src/api/system/person.js

@@ -16,17 +16,6 @@ export function updateUserInfo(params) {
     })
 }
 
-export function uploadPhoto(params) {
-    return request({
-        url: "/qdport-resource/oss/endpoint/put-file",
-        method: "POST",
-        data: params,
-        headers: {
-            "Content-Type": "multipart/form-data"
-        }
-    })
-}
-
 export function upChangePassword(params) {
     return request({
         url: "/qdport-system/sys/user/changePassword",

+ 0 - 2
src/assets/style/common.scss

@@ -89,11 +89,9 @@
         .tjm_card_table_right {
             flex: 1;
             width: 0;
-
         }
         .tjm_card_table_double_left{
             width: 43%;
-    
         }
         .tjm_card_table_double_right{
             width: 43%;

+ 24 - 0
src/components/Pagination/index.vue

@@ -0,0 +1,24 @@
+<template>
+	<el-pagination :layout="layout" :total="total" :page-size="pageSize" :page-sizes="pageSizes" v-model:currentPage="currentPage" @current-change="paginationChange" @update:page-size="pageSizeChange"></el-pagination>
+</template>
+
+<script setup>
+const $emit = defineEmits(["update:pageNo", "update:pageSize", "paginationChange"]);
+const props = defineProps({
+    total: { type: Number, default: 20 },
+    layout: { type: String, default: "total, sizes, prev, pager, next" },
+	pageNo: { type: Number, default: 1 },
+	pageSize: { type: Number, default: 10 },
+    pageSizes: { type: Array, default: () => [10, 20, 30, 40, 50] }, // [20, 30, 100]
+});
+
+const currentPage = ref(props.pageNo);
+const pageSizeChange = e => {
+    $emit("update:pageSize", e);
+    $emit("paginationChange");
+};
+const paginationChange = e => {
+    $emit("update:pageNo", e);
+    $emit("paginationChange");
+};
+</script>

+ 2 - 2
src/components/SvgIcon/svgicon.js

@@ -1,9 +1,9 @@
-import * as components from '@element-plus/icons-vue'
+import * as components from "@element-plus/icons-vue"
 export default {
     install: (app) => {
         for (const key in components) {
             const componentConfig = components[key];
             app.component(componentConfig.name, componentConfig);
         }
-    },
+    }
 };

+ 30 - 0
src/components/Upload/imageViewer.vue

@@ -0,0 +1,30 @@
+<template>
+	<div class="yh-image-viewer">
+		<el-image-viewer v-if="showViewer" v-bind="$attrs" :url-list="imageList" @close="$emit('closed')"></el-image-viewer>
+	</div>
+</template>
+
+<script>
+	import { ElImageViewer } from "element-plus";
+
+	export default {
+		emits: ["closed"],
+		components: {
+			ElImageViewer
+		},
+
+		props: {
+			showViewer: { type: Boolean, default: false },
+			imageList: { type: Array, default: () => [] }
+		},
+
+		data() {
+			return {}
+		},
+
+		methods: {}
+	}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 197 - 0
src/components/Upload/index.vue

@@ -0,0 +1,197 @@
+<template>
+	<div class="yh-upload-file">
+		<el-upload ref="uploader"
+			:class="hideAdd && 'el-upload-hide-add'"
+			v-model:file-list="defaultFileList"
+			action=""
+			:accept="accept"
+			:limit="limit"
+			:multiple="multiple"
+			:disabled="disabled"
+			show-file-list
+			:http-request="request"
+			:before-upload="before"
+			:before-remove="beforeRemove"
+			:on-success="success"
+			:on-error="error"
+			:on-preview="handlePreview"
+			:on-exceed="handleExceed">
+			<slot>
+				<el-button type="primary" :disabled="disabled">Click to upload</el-button>
+			</slot>
+			<template #tip>
+				<div v-if="tip" class="el-upload__tip">{{ tip }}</div>
+			</template>
+		</el-upload>
+		<span style="display: none!important;"><el-input v-model="value"></el-input></span>
+	</div>
+
+	<yh-image-viewer :showViewer="showPictureViewer" :imageList="previewImageList" teleported @close="showPictureViewer = false"></yh-image-viewer>
+</template>
+
+<script>
+  	import { defineAsyncComponent } from "vue";
+    import Folder from "@/api/folder.js";
+
+	export default {
+		components: {
+			yhImageViewer: defineAsyncComponent(() => import("@/components/Upload/imageViewer.vue")),
+		},
+		props: {
+			modelValue: { type: Array, default: () => [] },
+			tip: { type: String, default: "" },
+			accept: { type: String, default: "" },
+			maxSize: { type: Number, default: 50 },
+			limit: { type: Number, default: 0 },
+			multiple: { type: Boolean, default: true },
+			disabled: { type: Boolean, default: false },
+			hideAdd: { type: Boolean, default: false },
+			onSuccess: { type: Function, default: () => { return true } }
+		},
+
+		data() {
+			return {
+				value: "",
+				defaultFileList: [],
+				
+				showPictureViewer: false,
+				previewImageList: []
+			}
+		},
+
+		watch: {
+			modelValue(val) {
+				if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
+					this.defaultFileList = val;
+					this.value = val;
+				}
+			},
+
+			defaultFileList: {
+				deep: true,
+				handler(val) {
+					this.$emit("update:modelValue", this.formatArr(val));
+					this.value = val.map(v => v.path).join(",");
+				}
+			}
+		},
+
+		mounted() {
+			this.defaultFileList = this.modelValue;
+			this.value = this.modelValue;
+		},
+
+		methods: {
+			// 格式化数组值
+			formatArr(arr) {
+				return arr.map(item => ({ fileName: item.fileName, fileType: item.fileType, fileDomain: item.fileDomain, originalName: item.originalName, path: item.fileDomain + "/" + item.fileName }));
+			},
+
+			before(file) {
+				const maxSize = file.size / 1024 / 1024 < this.maxSize;
+				if (!maxSize) {
+					ElMessage.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
+					return false;
+				}
+			},
+
+			success(res, file) {
+				let os = this.onSuccess(res, file);
+				if (os != undefined && os == false) return false;
+			
+				file.fileDomain = res.domain;
+				file.fileName = res.name;
+				file.fileType = res.fileType;
+				file.originalName = res.originalName;
+				file.path = res.domain + "/" + res.name;
+			},
+
+			error(message) {
+				ElNotification.error({ title: "上传文件未成功", message });
+			},
+
+			beforeRemove({ id, name, fileName }) { // id, name, fileName
+				return ElMessageBox.confirm(`是否移除 ${name}? 此操作不可逆!`, "提示", {
+					type: "warning",
+					confirmButtonText: "移除"
+				}).then(() => {
+                    const promiseArray = [Folder.rm(fileName)];
+                    id && promiseArray.push(Folder.fileRemove(id));
+
+                    return Promise.all(promiseArray).then(() => true).catch(() => false);
+				}).catch(() => false);
+			},
+
+			handleExceed() {
+				ElMessage.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`);
+			},
+
+			handlePreview(uploadFile) {
+				if (["image/jpeg", "image/png"].includes(uploadFile.fileType)) {
+					this.showPictureViewer = true;
+					this.previewImageList = [uploadFile.path];
+				} else {
+                    Folder.download(uploadFile.path).then(res => {
+                        const a = document.createElement("a");
+                        a.href = URL.createObjectURL(res.data);
+                        a.download = uploadFile.name;
+                        a.click();
+                    });
+				}
+			},
+
+			request(param) {
+				const data = new FormData();
+				data.append(param.filename, param.file);
+
+                Folder.up(data, {
+					onUploadProgress: e => {
+						const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10);
+						param.onProgress({ percent });
+					}
+				}).then(res => {
+					if (res.code == 200) param.onSuccess({ ...res.data, fileType: param.file.type });
+					else param.onError(res.msg || "未知错误");
+				}).catch(err => param.onError(err));
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.el-form-item.is-error .yh-upload-file:deep(.el-upload-dragger) {
+  border-color: var(--el-color-danger);
+}
+
+.yh-upload-file {
+  width: 100%;
+
+  :deep(.el-upload-list__item) {
+    transition: none !important;
+  }
+
+  .el-upload-hide-add {
+    :deep(.el-upload) {
+      display: none;
+    }
+
+    :deep(.el-upload-list) {
+      margin-top: 0;
+
+      .el-upload-list__item {
+        &:hover {
+          background-color: transparent;
+        }
+
+        .el-upload-list__item-info {
+          width: 100%;
+        }
+
+        .el-upload-list__item-status-label {
+          display: none;
+        }
+      }
+    }
+  }
+}
+</style>

+ 15 - 15
src/main.js

@@ -1,16 +1,16 @@
-import { createApp } from 'vue'
-import { createPinia } from 'pinia'
-import piniaPluginPersist from 'pinia-plugin-persist'
-import App from './App.vue'
-import router from './router'
-import './permission'
-import '@/micro/index.js'
-import i18n from '@/i18n'
-import '@/assets/style/index.scss'
-import 'virtual:svg-icons-register'
-import SvgIcon from '@/components/SvgIcon/index.vue'
-import elementIcons from '@/components/SvgIcon/svgicon.js'
-import directive from './directive'
+import { createApp } from "vue"
+import { createPinia } from "pinia"
+import piniaPluginPersist from "pinia-plugin-persist"
+import App from "./App.vue"
+import router from "./router"
+import "./permission"
+import "@/micro/index.js"
+import i18n from "@/i18n"
+import "@/assets/style/index.scss"
+import "virtual:svg-icons-register"
+import SvgIcon from "@/components/SvgIcon/index.vue"
+import elementIcons from "@/components/SvgIcon/svgicon.js"
+import directive from "./directive"
 
 const pinia = createPinia()
 pinia.use(piniaPluginPersist)
@@ -19,6 +19,6 @@ directive(app)
 app.use(pinia)
 app.use(router)
 app.use(elementIcons)
-app.component('svg-icon', SvgIcon)
+app.component("svg-icon", SvgIcon)
 app.use(i18n)
-app.mount('#app')
+app.mount("#app")

+ 13 - 16
src/permission.js

@@ -1,24 +1,22 @@
-import router from './router'
-import { getToken } from '@/utils/auth'
-import { useUserStore } from '@/store/user.js'
-import { useSettingStore } from '@/store/settings.js'
-import { usePermissionStore } from '@/store/permission.js'
-import { dynamicRoutes } from '@/router/dynamicRoutes'
+import router from "./router"
+import { getToken } from "@/utils/auth"
+import { useUserStore } from "@/store/user.js"
+import { useSettingStore } from "@/store/settings.js"
+import { usePermissionStore } from "@/store/permission.js"
+import { dynamicRoutes } from "@/router/dynamicRoutes"
 
-const whiteList = ['/login', '/yh', '/ssoyh']
+const whiteList = ["/login", "/yh", "/ssoyh"]
 router.beforeEach((to, from, next) => {
     if (getToken()) {
         to.meta.title && useSettingStore().setTitle(to.meta.title)
         if (useUserStore().roles.length === 0) {
             useUserStore().RefreshInfo().then(res => {
                 usePermissionStore().generateRoutes().then(accessRoutes => {
-                    accessRoutes.forEach(item => {
-                        router.addRoute(item)
-                    })
+                    accessRoutes.forEach(item => router.addRoute(item))
                     router.addRoute({
                         path: "/:pathMatch(.*)*",
-                        name: 'notFound',
-                        redirect: '/notfound',
+                        name: "notFound",
+                        redirect: "/notfound"
                     })
                     next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
                 })
@@ -26,15 +24,14 @@ router.beforeEach((to, from, next) => {
         } else {
             next()
         }
-    } else { //登录
-        //白名单放行
+    } else { // 登录
+        // 白名单放行
         if (whiteList.indexOf(to.path) !== -1) {
             next()
         } else {
             next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
         }
     }
-
 })
 
 router.afterEach(() => {
@@ -42,5 +39,5 @@ router.afterEach(() => {
 })
 
 router.onError((error) => {
-    console.warn('路由错误', error.message)
+    console.warn("路由错误", error.message)
 })

+ 2 - 5
src/router/constantRoutes.js

@@ -53,7 +53,6 @@ export const staticRoutes = [
         redirect: "/index",
         alwaysShow: false,
         children: [
-
             {
                 path: "/index",
                 name: "Index",
@@ -94,7 +93,5 @@ export const staticRoutes = [
                 children: []
             }
         ]
-    },
-
-
-]
+    }
+]

+ 6 - 3
src/store/permission.js

@@ -157,12 +157,15 @@ function filterAsyncRouter(asyncRouterMap, httpSave = true) {
             }
         }
     })
-    return arr
 
+    return arr
 }
 
 function loadView(view) {
     for (const path in modules) {
-        if (path.includes(view)) return modules[path]
+        const dir = "/" + path.split("views/")[1].split(".vue")[0];
+        if (dir === view || dir == view + "/index") {
+            return modules[path]
+        }
     }
-}
+}

+ 2 - 8
src/store/settings.js

@@ -9,12 +9,7 @@ export const useSettingStore = defineStore("userSetting", {
     }),
     persist: {
         enabled: true,
-        strategies: [
-            {
-                key: "useSetting",
-                storage: localStorage,
-            },
-        ],
+        strategies: [{ key: "useSetting", storage: localStorage }]
     },
     getters: {},
     actions: {
@@ -23,5 +18,4 @@ export const useSettingStore = defineStore("userSetting", {
             document.title = getConfig("projectName") + "-" + title
         }
     }
-})
-
+})

+ 2 - 3
src/store/tabsbar.js

@@ -10,7 +10,6 @@ export const useTabsBarStore = defineStore(
             sidebarRouters: computed(() => usePermissionStore().sidebarRouters)
         }),
         actions: {
-
             addVisitedView(view) {
                 if (this.visitedViews.some(v => v.path === view.path)) return
                 const viewDeal = JSON.parse(JSON.stringify(view))
@@ -191,6 +190,6 @@ export const useTabsBarStore = defineStore(
                     resolve([...this.visitedViews])
                 })
             }
-        },
+        }
     }
-)
+)

+ 0 - 1
src/store/user.js

@@ -147,7 +147,6 @@ export const useUserStore = defineStore(
                     removeToken()
                     removeRefreshToken()
                     resolve()
-
                 })
             }
         }

+ 124 - 0
src/views/policyShare/approve.vue

@@ -0,0 +1,124 @@
+<template>
+    <el-dialog v-model="visible" :title="titleMap[mode]" :width="500" top="20vh" @closed="$emit('closed', mode)">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                <template v-if="mode == 'agree'">
+                    <el-col :span="24">
+                        <el-form-item label="分值(0-10)" prop="score">
+                            <el-input-number v-model="form.score" :min="0" :max="10" :step="0.1" :precision="2" controls-position="right" placeholder="请输入分值"></el-input-number>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <el-form-item label="是否入库" prop="isInWh">
+                            <el-radio-group v-model="form.isInWh" @change="radioChange">
+                                <el-radio v-for="(label, key) in inWHDic" :key="key" :label="label" :value="key"></el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                    <el-col v-if="form.isInWh == 1" :span="24">
+                        <el-form-item label="入库类型" prop="inWhType">
+                            <el-select v-model="form.inWhType" placeholder="请选择入库类型">
+                                <el-option v-for="item in storageTypeDic" :key="item" :label="item" :value="item"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </template>
+
+                <el-col v-if="mode == 'disagree'" :span="24">
+                    <el-form-item label="意见" prop="remark">
+                        <el-input v-model="form.remark" type="textarea" :rows="4" placeholder="请输入意见"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+
+        <template #footer>
+            <el-button type="primary" @click="submit">确 定</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+import API from "@/api/policy/share.js";
+import { policyShareDict } from "./main.js";
+
+const $emit = defineEmits(["closed"]);
+const { storageTypeDic, inWHDic } = policyShareDict();
+
+const visible = ref(false);
+const mode = ref("agree");
+const titleMap = reactive({
+    agree: "考核评分",
+    disagree: "退回意见"
+});
+
+let form = reactive({
+    score: null,
+    isInWh: "0",
+    inWhType: null,
+    remark: "不同意"
+});
+const rules = reactive({
+    score: [{ required: true, message: "请输入分值" }],
+    isInWh: [{ required: true }],
+    inWhType: [{ required: true, message: "请选择入库类型" }],
+    remark: [{ required: true, message: "请输入退回意见" }]
+});
+
+const init = (modeValue = "agree", data) => {
+    mode.value = modeValue;
+    visible.value = true;
+
+    form = Object.assign(data, form);
+}
+
+const formRef = ref();
+const resetForm = () => {
+    if (!formRef || !formRef.value) return;
+
+    form = reactive({
+        score: null,
+        isInWh: "0",
+        inWhType: null,
+        remark: "不同意"
+    });
+    formRef.value.resetFields();
+}
+
+const submit = () => {
+    if (!formRef || !formRef.value) return;
+
+    formRef.value.validate(valid => {
+        if (valid) {
+            API[mode.value](form).then(res => {
+                if (res.code === 200) {
+                    visible.value = false;
+                    ElMessage.success("操作成功");
+                } else ElMessage.error(res.msg);
+            });
+        } else {
+            return false;
+        }
+    });
+}
+
+const radioChange = e => {
+    form.inWhType = null;
+}
+
+defineExpose({ init, resetForm });
+</script>
+
+<style lang="scss" scoped>
+.el-input-number {
+  width: 100%;
+
+  :deep(.el-input) .el-input__wrapper {
+    padding: 1px 11px;
+
+    .el-input__inner {
+      text-align: unset;
+    }
+  }
+}
+</style>

+ 274 - 0
src/views/policyShare/dialog.vue

@@ -0,0 +1,274 @@
+<template>
+    <el-dialog v-model="visible" :title="titleMap[mode]" :width="680" @closed="$emit('closed', mode)">
+        <el-form ref="formRef" :model="form" :rules="rules" :disabled="mode == 'detail' || mode == 'approve'" label-width="100px">
+            <el-row>
+                <el-col v-if="form.id" :span="12">
+                    <el-form-item label="政策编号" prop="businessNo">
+                        <el-input v-model="form.businessNo" disabled></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="填报人">
+                        <el-input v-model="form.createName" disabled></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="填报部门">
+                        <el-input v-model="form.deptName" disabled></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="填报单位">
+                        <el-input v-model="form.companyName" disabled></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col v-if="form.id" :span="12">
+                    <el-form-item label="填报时间">
+                        <el-date-picker v-model="form.createTime" disabled format="YYYY-MM-DD HH:mm:ss"></el-date-picker>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="政策名称" prop="name">
+                        <el-input v-model="form.name" placeholder="请输入政策名称"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="政策等级" prop="zcLevel">
+                        <el-select v-model="form.zcLevel" placeholder="请选择政策等级">
+                            <el-option v-for="item in levelDic" :key="item" :label="item" :value="item"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="政策类别" prop="zcType">
+                        <el-select v-model="form.zcType" placeholder="请选择政策类别">
+                            <el-option v-for="item in typeDic" :key="item" :label="item" :value="item"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="政策文号" prop="docNo">
+                        <el-input v-model="form.docNo" placeholder="请输入政策文号"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="联系方式" prop="contactPhone">
+                        <el-input v-model="form.contactPhone" placeholder="请输入联系方式"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                    <el-form-item label="政策概要" prop="abstractContent">
+                        <el-input v-model="form.abstractContent" type="textarea" :rows="4" placeholder="请输入政策概要"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                    <el-form-item :class="(mode == 'detail' || mode == 'approve') && 'no-upload-btn'" label="附件">
+                        <yhUpload v-model="form.fileList" :limit="10">
+                            <el-button type="primary" icon="upload" size="small"></el-button>
+                        </yhUpload>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+
+            <el-row v-if="form.status == 'done' || form.status == 'inactive'">
+                <template v-if="form.status == 'done'">
+                    <el-col :span="24">
+                        <el-form-item label="分值" prop="score">
+                            <el-input v-model="form.score"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <el-form-item label="是否入库" prop="isInWh">{{ form.isInWh == 1 && "是" || "否" }}</el-form-item>
+                    </el-col>
+                    <el-col v-if="form.isInWh == 1" :span="24">
+                        <el-form-item label="入库类型" prop="inWhType">
+                            <el-input v-model="form.inWhType"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </template>
+
+                <el-col v-else :span="24">
+                    <el-form-item label="意见" prop="remark">
+                        <el-input v-model="form.remark" type="textarea" :rows="4" placeholder="请输入意见"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+
+        <template #footer>
+            <template v-if="mode == 'approve' && form.status == 'approve'">
+                <el-button type="primary" @click="policyApprove('agree')">通 过</el-button>
+                <el-button type="primary" @click="policyApprove('disagree')">退 回</el-button>
+            </template>
+            <template v-if="mode == 'add' || mode == 'edit'">
+                <el-button type="primary" @click="validateForm(mode)">保 存</el-button>
+                <el-button type="primary" @click="validateForm('saveApprove')">直接上报</el-button>
+            </template>
+        </template>
+    </el-dialog>
+
+    <approve-detail ref="approveRef" @closed="form_reset"></approve-detail>
+</template>
+
+<script setup>
+import API from "@/api/policy/share.js";
+import { useUserStore } from "@/store/user.js";
+import { policyShareDict } from "./main.js";
+import yhUpload from "@/components/Upload/index.vue";
+import approveDetail from "./approve.vue";
+
+const $emit = defineEmits(["closed"]);
+const { userInfo } = storeToRefs(useUserStore()); // store 用户
+const { levelDic, typeDic, inWHDic } = policyShareDict();
+
+const visible = ref(false);
+const mode = ref("add");
+const titleMap = reactive({
+    add: "新增政策",
+    edit: "编辑政策",
+    detail: "政策详情",
+    approve: "政策详情"
+});
+
+let form = reactive({
+    createId: userInfo.value.id,
+    createName: userInfo.value.name,
+    companyName: null,
+    deptName: null,
+    name: null,
+    zcLevel: null,
+    zcType: null,
+    docNo: null,
+    contactPhone: null,
+    abstractContent: null,
+    fileList: []
+});
+const rules = reactive({
+    businessNo: [{ required: true }],
+    name: [{ required: true, message: "请输入政策名称" }],
+    zcLevel: [{ required: true, message: "请选择政策等级" }],
+    zcType: [{ required: true, message: "请选择政策类别" }],
+    docNo: [{ required: true, message: "请输入政策文号" }],
+    contactPhone: [{ required: true, message: "请输入联系方式" }],
+    abstractContent: [{ required: true, message: "请输入政策概要" }],
+    score: [{ required: true, message: "请输入分值" }],
+    isInWh: [{ required: true }],
+    inWhType: [{ required: true, message: "请选择入库类型" }],
+});
+
+onMounted(() => {
+    API.getUserDept(userInfo.value.deptId).then(res => {
+        if (res.code === 200) {
+            form.companyName = res.data.companyName;
+            form.deptName = res.data.deptName;
+        }
+    });
+})
+
+const init = (modeValue = "add", data) => {
+    mode.value = modeValue;
+    visible.value = true;
+
+    if (data) {
+        form.updateId = userInfo.value.id;
+        setData(data);
+    }
+}
+const setData = id => {
+    API.detail(id).then(res => {
+        if (res.code === 200) {
+            const rowData = reactive({
+                id: null,
+                businessNo: null,
+                createId: null,
+                createName: null,
+                companyName: null,
+                deptName: null,
+                createTime: null,
+                name: null,
+                zcLevel: null,
+                zcType: null,
+                docNo: null,
+                contactPhone: null,
+                abstractContent: null,
+                status: null,
+                score: null,
+                isInWh: "0",
+                inWhType: null,
+                fileList: []
+            });
+            for (const key in rowData) {
+                if (key == "fileList") {
+                    rowData[key] = res.data[key].map(file => ({ ...file, name: file.originalName, path: file.fileDomain + "/" + file.fileName }));
+                } else rowData[key] = res.data[key] || null;
+            }
+            form = Object.assign(form, rowData);
+            console.log(form)
+        } else ElMessage.error(res.msg);
+    });
+}
+
+const formRef = ref();
+const validateForm = modeValue => {
+    if (!formRef || !formRef.value) return;
+
+    formRef.value.validate(valid => {
+        if (valid) {
+            submit(modeValue);
+        } else {
+            return false;
+        }
+    });
+}
+const resetForm = () => {
+    if (!formRef || !formRef.value) return;
+
+    form = reactive({
+        createId: userInfo.value.id,
+        createName: userInfo.value.name,
+        companyName: null,
+        deptName: null,
+        name: null,
+        zcLevel: null,
+        zcType: null,
+        docNo: null,
+        contactPhone: null,
+        abstractContent: null,
+        fileList: []
+    });
+    formRef.value.resetFields();
+}
+
+const submit = modeValue => {
+    API[modeValue](form).then(res => {
+        if (res.code === 200) {
+            visible.value = false;
+            ElMessage.success("操作成功");
+        } else ElMessage.error(res.msg);
+    });
+}
+
+const approveRef = ref();
+const policyApprove = (modeValue) => {
+    approveRef.value.init(modeValue, form);
+}
+const form_reset = () => {
+    setData(form.id);
+    approveRef.value.resetForm();
+}
+
+
+defineExpose({ init, resetForm });
+</script>
+
+<style lang="scss" scoped>
+.no-upload-btn :deep(.yh-upload-file) {
+  .el-upload {
+    display: none;
+  }
+
+  .el-upload-list.is-disabled {
+    margin-top: 0;
+  }
+}
+</style>

+ 157 - 15
src/views/policyShare/index.vue

@@ -3,41 +3,183 @@
         <div class="tjm_card_title">查询表格</div>
         <div class="tjm_card_select">
             <el-form class="tjm_card_select_left" :model="params" inline label-width="80px" label-position="left">
-                <el-form-item label="用户名称">
-                    <el-input v-model="params.userName" clearable></el-input>
+                <el-form-item label="政策名称">
+                    <el-input v-model="params.name" clearable placeholder="请输入政策名称"></el-input>
                 </el-form-item>
-                <el-form-item label="姓名">
-                    <el-input v-model="params.realName" clearable></el-input>
+                <el-form-item label="政策等级">
+                    <el-select v-model="params.zcLevel" clearable placeholder="请选择政策等级">
+                        <el-option v-for="item in levelDic" :key="item" :label="item" :value="item"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="政策类别">
+                    <el-select v-model="params.zcType" clearable placeholder="请选择政策类别">
+                        <el-option v-for="item in typeDic" :key="item" :label="item" :value="item"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="状态">
+                    <el-select v-model="params.status" clearable placeholder="请选择状态">
+                        <el-option v-for="(label, key) in statusDic" :key="key" :label="label" :value="key"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="是否入库">
+                    <el-select v-model="params.isInWh" clearable placeholder="请选择入库状态">
+                        <el-option v-for="(label, key) in inWHDic" :key="key" :label="label" :value="key"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="入库类别">
+                    <el-select v-model="params.inWhType" clearable placeholder="请选择入库类别">
+                        <el-option v-for="item in storageTypeDic" :key="item" :label="item" :value="item"></el-option>
+                    </el-select>
                 </el-form-item>
 
                 <el-form-item>
-                    <el-button type="primary" icon="plus" @click="search">搜索</el-button>
+                    <el-button type="primary" icon="search" @click="reloadTable">搜索</el-button>
                     <el-button icon="refresh-right" @click="reset">重置</el-button>
                 </el-form-item>
             </el-form>
         </div>
         <el-divider></el-divider>
+
+        <div class="tjm_card_tools">
+            <div class="tjm_card_tools_left">
+                <el-button type="primary" icon="plus" @click="table_add">新增</el-button>
+                <el-button type="primary" icon="upload">导入</el-button>
+            </div>
+            <div class="tjm_card_tools_right">
+                <el-button icon="upload-filled">导出</el-button>
+            </div>
+        </div>
+        <div class="tjm_card_table">
+            <el-table v-loading="loading" row-key="id" header-cell-class-name="tjm_card_table_header" height="450" :data="tableData" border>
+                <el-table-column type="index" width="50"></el-table-column>
+                <el-table-column label="政策编号" prop="businessNo" width="180"></el-table-column>
+                <el-table-column label="状态" width="180">
+                    <template #default="scope">{{ formatStatus(scope.row) }}</template>
+                </el-table-column>
+                <el-table-column label="政策名称" prop="name" width="180"></el-table-column>
+                <el-table-column label="政策等级" prop="zcLevel" width="180"></el-table-column>
+                <el-table-column label="政策类别" prop="zcType" width="180"></el-table-column>
+                <el-table-column label="政策文号" prop="docNo" width="180"></el-table-column>
+                <el-table-column label="填报人" prop="createName" width="180"></el-table-column>
+                <el-table-column label="填报单位" prop="companyName" width="180"></el-table-column>
+                <el-table-column label="填报时间" prop="createTime" width="180"></el-table-column>
+                <el-table-column label="联系方式" prop="contactPhone" width="180"></el-table-column>
+                <el-table-column label="考核评分" prop="score" width="180"></el-table-column>
+                <el-table-column label="是否入库" width="180">
+                    <template #default="scope">{{ formatInWh(scope.row.isInWh) }}</template>
+                </el-table-column>
+                <el-table-column label="入库类别" prop="inWhType" width="180"></el-table-column>
+                <el-table-column label="操作" fixed="right" width="260">
+                    <template #default="scope">
+                        <template v-if="scope.row.status == 'active' || scope.row.status == 'inactive'">
+                            <el-button type="primary" icon="edit" link @click="table_edit(scope.row)">修改</el-button>
+                            <el-button type="primary" icon="edit" link @click="table_update(scope.row, 'saveApprove')">提交</el-button>
+                            <el-button type="primary" icon="delete" link @click="table_del(scope.row)">删除</el-button>
+                        </template>
+                        <el-button v-if="scope.row.status == 'approve' || scope.row.status == 'done'" type="primary" link @click="table_edit(scope.row, 'detail')">详情</el-button>
+                        <el-button v-if="scope.row.status == 'approve'" type="primary" link @click="table_update(scope.row, 'withdraw')">撤回</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+        <div class="tjm_card_pagination">
+            <yh-pagination v-model:pageNo="params.page" v-model:pageSize="params.size" :total="total" @paginationChange="reloadTable"></yh-pagination>
+        </div>
     </el-card>
 
-    <!-- <plan-detail v-if="dialog" ref="planDetail" @success="reloadTable" @closed="dialog = false"></plan-detail> -->
+    <policy-detail ref="detailRef" @closed="form_reset"></policy-detail>
 </template>
 
 <script setup>
-// import {} from '@/api/system/user.js'
-const { proxy } = getCurrentInstance();
+import API from "@/api/policy/share.js"
+import { policyShareDict } from "./main.js";
+import yhPagination from "@/components/Pagination/index.vue";
+import PolicyDetail from "./dialog.vue";
 
+const loading = ref(false);
 const params = reactive({
     page: 1,
-    size: 10,
-    userName: null,
-    realName: null
+    size: 10
 });
 const total = ref(0);
-const tableData = reactive([]);
+let tableData = reactive([]);
+const { levelDic, typeDic, statusDic, inWHDic, storageTypeDic } = policyShareDict();
+
+// table format
+const formatStatus = ({ status, isInWh }) => {
+    if (status == "done" && isInWh == 1) return "审核入库"
+    return statusDic[status] || "";
+}
+const formatInWh = isInWh => {
+    return inWHDic[isInWh] || "";
+}
+// table format
 
-// watch(deptName, val => {
-//   proxy.$refs['deptTreeRef'].filter(val)
-// });
+const reset = () => {
+    for (const key in params) {
+        if (key == "page") params[key] = 1;
+        else if (key == "size") params[key] = 10;
+        else params[key] = null;
+    }
+    reloadTable();
+}
+
+const reloadTable = (mode = "add") => {
+    if (mode == "add") params.page = 1;
+
+    loading.value = true;
+    API.get(params).then(res => {
+        loading.value = false;
+        if (res.code === 200) {
+            tableData = reactive(res.data.records);
+            total.value = res.data.total;
+        } else ElMessage.error(res.msg);
+    }).catch(() => loading.value = false);
+}
+reloadTable();
+
+// 弹出框
+const detailRef = ref();
+const form_reset = mode => {
+    detailRef.value.resetForm();
+    reloadTable(mode);
+}
+const table_add = () => {
+    detailRef.value.init();
+}
+const table_edit = (row, mode = "edit") => {
+    detailRef.value.init(mode, row.id);
+}
+const table_del = row => {
+    ElMessageBox.confirm("是否确认删除?", "删除警告", {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API.del({ ids: row.id }).then(res => {
+            if (res.code == 200) {
+                ElMessage.success(res.msg);
+                reloadTable();
+            } else ElMessage.warning(res.msg);
+        });
+    });
+}
+
+const table_update = (row, mode) => {
+    const msg = mode == "withdraw" && "撤回" || "提交";
+    ElMessageBox.confirm(`是否确认${msg}?`, `${msg}警告`, {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API[mode](row).then(res => {
+            if (res.code === 200) {
+                ElMessage.success(`已${msg}`);
+                reloadTable();
+            } else ElMessage.error(res.msg);
+        });
+    });
+}
 </script>
 
 <style lang="scss" scoped>

+ 14 - 0
src/views/policyShare/main.js

@@ -0,0 +1,14 @@
+export function policyShareDict() {
+    const levelDic = reactive(["国家级", "省部级", "市区级"]);
+    const typeDic = reactive(["财税政策", "人才支持", "市场开拓", "智慧创新", "招商引资", "技术改造", "法律法规", "营商环境", "企业管理"]);
+    const statusDic = reactive({
+        active: "保存",
+        approve: "审核",
+        done: "审核通过",
+        inactive: "退回"
+    });
+    const storageTypeDic = reactive(["政策类", "解读类"]);
+    const inWHDic = reactive({ 0: "否", 1: "是" });
+
+    return { levelDic, typeDic, statusDic, storageTypeDic, inWHDic }
+}

+ 19 - 31
src/views/system/dept.vue

@@ -71,19 +71,21 @@
     <!--      </div>-->
             <div class="tjm_cart_table">
                 <el-table
+                    ref="deptTable"
                     header-cell-class-name="tjm_cart_table_header"
                     height="400"
                     :data="tableData"
                     v-loading="loading"
                     border
-                    lazy
-                    :load="loadTalbeTree"
-                    style="width: 100%"
-                    :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
                     row-key="id"
                 >
                     <!-- <el-table-column type="selection" width="55" /> -->
                     <el-table-column prop="name" label="部门名称" />
+                    <el-table-column label="类型">
+                        <template #default="scope">
+                            {{ scope.row.deptType == 1 && "单位" || "部门" }}
+                        </template>
+                    </el-table-column>
                     <el-table-column prop="sort" label="排序" />
                     <el-table-column prop="ope" label="操作">
                         <template #default="scope">
@@ -139,6 +141,14 @@
                             />
                         </el-form-item>
                     </el-col>
+                    <el-col :span="24">
+                        <el-form-item label="类型" prop="deptType">
+                            <el-radio-group v-model="form.deptType">
+                                <el-radio label="部门" :value="0"></el-radio>
+                                <el-radio label="单位" :value="1"></el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
                     <el-col :span="24">
                         <el-form-item label="部门排序" prop="sort">
                             <el-input-number v-model="form.sort" :min="0" :max="1000" />
@@ -172,16 +182,17 @@ const params = ref({
 })
 const isExpandAll = ref(true) //展  开
 const tableData = ref([])  //展示数据
-const allTableData = ref([]) //备份全量数据
 
 const dialogTitle = ref('新增')
 const addShow = ref(false)
+const deptTable = ref()
 
 const form = ref({
     name: null,
     code: null,
     state: true,
     description: null,
+    deptType: 0,
     sort: 0,
 })
 const rules = ref({
@@ -191,37 +202,12 @@ const rules = ref({
 
 const { proxy } = getCurrentInstance()
 
-function loadTalbeTree(tree, treeNode, resolve) {
-    const idCopy = JSON.parse(JSON.stringify(tree.idList))
-    let resolveArr = allTableData.value
-    let id
-    while (id = tree.idList.shift()) {
-        const tarItem = resolveArr.find(item => item.id === id)
-        tarItem.loadedChildren = true
-        resolveArr = tarItem.children
-    }
-    resolveArr = JSON.parse(JSON.stringify(resolveArr))
-    resolveArr.forEach(item => {
-        item.hasChildren = item.children && item.children.length > 0
-        item.children = null
-        item.idList = JSON.parse(JSON.stringify(idCopy))
-        item.idList.push(item.id)
-    })
-    tree.loadedChildren = true
-    resolve(resolveArr)
-}
 function getList() {
     loading.value = true
     getMainList().then(res => {
         loading.value = false
         if (res.code === 200) {
-            allTableData.value = res.data || []
-            tableData.value = JSON.parse(JSON.stringify(res.data)).map(item => {
-                item.hasChildren = item.children && item.children.length > 0
-                item.children = null
-                item.idList = [item.id]
-                return item
-            })
+            tableData.value = res.data
         }
     }).catch(() => {
         loading.value = false
@@ -247,6 +233,7 @@ function editItem(row) {
     form.value.code = row.code
     form.value.description = row.description
     form.value.sort = row.sort
+    form.value.deptType = row.deptType
     form.value.state = row.state === 0
     dialogTitle.value = '编辑'
     addShow.value = true
@@ -261,6 +248,7 @@ function resetForm() {
         code: null,
         state: true,
         description: null,
+        deptType: 1,
         sort: 0
     }
     if (proxy.$refs['userRef']) {

+ 3 - 3
src/views/system/userInfo.vue

@@ -122,7 +122,8 @@
 </template>
 
 <script setup>
-import { getUserInfo, upChangePassword, updateUserInfo, uploadPhoto } from '@/api/system/person'
+import { getUserInfo, upChangePassword, updateUserInfo } from '@/api/system/person'
+import Folder from "@/api/folder.js";
 import { Encrypt } from '@/utils/aes'
 import { useUserStore } from '@/store/user.js'
 import router from '@/router'
@@ -270,8 +271,7 @@ function cancelPassword() {
 function customRequest(content) {
     const dFormData = new FormData()
     dFormData.append('file', content.file)
-    console.log(content.file, '12312')
-    uploadPhoto(dFormData).then((res) => {
+    Folder.up(dFormData).then((res) => {
         if (res.code == '200') {
             form.value.avatar = res.data.link
         }

+ 98 - 0
src/views/toDo/index.vue

@@ -0,0 +1,98 @@
+<template>
+    <el-card class="tjm_card_style_custom">
+        <div class="tjm_card_title">查询表格</div>
+        <div class="tjm_card_select">
+            <el-form class="tjm_card_select_left" :model="params" inline label-width="80px" label-position="left">
+                <el-form-item label="政策名称">
+                    <el-input v-model="params.zcName" clearable placeholder="请输入政策名称"></el-input>
+                </el-form-item>
+                <el-form-item label="政策类别">
+                    <el-select v-model="params.zcType" clearable placeholder="请选择政策类别">
+                        <el-option v-for="item in typeDic" :key="item" :label="item" :value="item"></el-option>
+                    </el-select>
+                </el-form-item>
+
+                <el-form-item>
+                    <el-button type="primary" icon="search" @click="reloadTable">搜索</el-button>
+                    <el-button icon="refresh-right" @click="reset">重置</el-button>
+                </el-form-item>
+            </el-form>
+        </div>
+        <el-divider></el-divider>
+
+        <div class="tjm_card_table">
+            <el-table v-loading="loading" row-key="id" header-cell-class-name="tjm_card_table_header" height="450" :data="tableData" border>
+                <el-table-column type="index" width="50"></el-table-column>
+                <el-table-column label="政策名称" prop="zcName" width="180"></el-table-column>
+                <el-table-column label="政策概要" prop="zcAbstractContent" width="180" show-overflow-tooltip></el-table-column>
+                <el-table-column label="政策类别" prop="zcType" width="180"></el-table-column>
+                <el-table-column label="填报人" prop="zcCreateName" width="180"></el-table-column>
+                <el-table-column label="填报单位" prop="zcCompanyName" width="180"></el-table-column>
+                <el-table-column label="联系方式" prop="zcContactPhone" width="180"></el-table-column>
+                <el-table-column label="填报时间" prop="zcCreateTime" width="180"></el-table-column>
+                <el-table-column label="操作" width="100">
+                    <template #default="scope">
+                        <el-button type="primary" link @click="table_detail(scope.row)">审核</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+        <div class="tjm_card_pagination">
+            <yh-pagination v-model:pageNo="params.page" v-model:pageSize="params.size" :total="total" @paginationChange="reloadTable"></yh-pagination>
+        </div>
+    </el-card>
+
+    <policy-share-detail ref="shareDetailRef" @closed="form_reset"></policy-share-detail>
+</template>
+
+<script setup>
+import API from "@/api/policy/todo.js"
+import { policyShareDict } from "@/views/policyShare/main.js";
+import yhPagination from "@/components/Pagination/index.vue";
+import PolicyShareDetail from "@/views/policyShare/dialog.vue";
+
+const loading = ref(false);
+const params = reactive({
+    page: 1,
+    size: 10
+});
+const total = ref(0);
+let tableData = reactive([]);
+const { typeDic } = policyShareDict();
+
+const reset = () => {
+    for (const key in params) {
+        if (key == "page") params[key] = 1;
+        else if (key == "size") params[key] = 10;
+        else params[key] = null;
+    }
+    reloadTable();
+}
+
+const reloadTable = () => {
+    loading.value = true;
+    API.get(params).then(res => {
+        loading.value = false;
+        if (res.code === 200) {
+            tableData = reactive(res.data.records);
+            total.value = res.data.total;
+        } else ElMessage.error(res.msg);
+    }).catch(() => loading.value = false);
+}
+reloadTable();
+
+// 弹出框
+const shareDetailRef = ref();
+const form_reset = () => {
+    shareDetailRef && shareDetailRef.value && shareDetailRef.value.resetForm();
+    reloadTable();
+}
+const table_detail = ({ refId, refType }) => {
+    refType == "policy_share" && shareDetailRef.value.init("approve", refId);
+}
+</script>
+
+<style lang="scss" scoped>
+.policy-container {
+}
+</style>

+ 43 - 49
vite.config.js

@@ -1,55 +1,56 @@
-import { fileURLToPath, URL } from 'node:url'
-import { defineConfig, loadEnv } from 'vite'
-import vue from '@vitejs/plugin-vue'
-import AutoImport from 'unplugin-auto-import/vite'
-import Components from 'unplugin-vue-components/vite'
-import Icons from "unplugin-icons/vite"
-import { ElementPlusResolver, VueUseComponentsResolver, VueUseDirectiveResolver } from 'unplugin-vue-components/resolvers'
-import IconsResolver from "unplugin-icons/resolver"
-import { FileSystemIconLoader } from "unplugin-icons/loaders"
-import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
-import path from 'path'
+import { fileURLToPath, URL } from "node:url";
+import { defineConfig, loadEnv } from "vite";
+import vue from "@vitejs/plugin-vue";
+import AutoImport from "unplugin-auto-import/vite";
+import Components from "unplugin-vue-components/vite";
+import Icons from "unplugin-icons/vite";
+import { ElementPlusResolver, VueUseComponentsResolver, VueUseDirectiveResolver } from "unplugin-vue-components/resolvers";
+import IconsResolver from "unplugin-icons/resolver";
+import { FileSystemIconLoader } from "unplugin-icons/loaders";
+import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
+import path from "path";
+
 // https://vitejs.dev/config/
 export default defineConfig(({ mode }) => {
-    const viteEnv = loadEnv(mode, './')
+    const viteEnv = loadEnv(mode, "./");
     return {
         base: viteEnv.VITE_BASE,
         server: {
-            host: '0.0.0.0',
-            port: '8080',
-            open: true,
+            host: "0.0.0.0",
+            port: "8080",
+            open: false,
             strictPort: true,
             cors: true,
             proxy: {
                 [viteEnv.VITE_BASE_URL]: {
                     target: viteEnv.VITE_BASE_SERVER_URL,
                     changeOrigin: true,
-                    rewrite: path => path.replace(viteEnv.VITE_BASE_URL, '/')
+                    rewrite: path => path.replace(viteEnv.VITE_BASE_URL, "/")
                 },
-                // '/api': {
+                // "/api": {
                 //   target: "http://qdport.gateway.10.236.3.36.nip.io",
                 //   changeOrigin: true,
-                //   rewrite: path => path.replace('/api', '/')
+                //   rewrite: path => path.replace("/api", "/")
                 // }
             }
         },
         build: {
-            outDir: 'dist',
-            assetsDir: 'static/assets',
+            outDir: "dist",
+            assetsDir: "static/assets",
             chunkSizeWarningLimit: 2000,
             rollupOptions: {
                 output: {
-                    chunkFileNames: 'static/js/[name]-[hash].js',
-                    entryFileNames: 'static/js/[name]-[hash].js',
-                    assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
+                    chunkFileNames: "static/js/[name]-[hash].js",
+                    entryFileNames: "static/js/[name]-[hash].js",
+                    assetFileNames: "static/[ext]/[name]-[hash].[ext]"
                 }
             }
         },
         resolve: {
             alias: {
-                '~': path.resolve(__dirname, './'),
-                '@': path.resolve(__dirname, './src')
-            },
+                "~": path.resolve(__dirname, "./"),
+                "@": path.resolve(__dirname, "./src")
+            }
         },
         css: {
             preprocessorOptions: {
@@ -71,21 +72,21 @@ export default defineConfig(({ mode }) => {
                     /\.[tj]sx?$/,
                     /\.vue$/,
                     /\.vue\?vue/,
-                    /\.md$/ // 
+                    /\.md$/
                 ],
-                imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'],
+                imports: ["vue", "pinia", "vue-router", "@vueuse/core"],
                 eslintrc: {
                     enabled: true,
-                    filepath: './.eslintrc-auto-import.json',
+                    filepath: "./.eslintrc-auto-import.json",
                     globalsPropValue: true
                 },
                 resolvers: [
                     ElementPlusResolver(),
-                    IconsResolver({ componentPrefix: "tjm-icon", enabledCollections: ["ep", "home", "user"], })
-                ],
+                    IconsResolver({ componentPrefix: "tjm-icon", enabledCollections: ["ep", "home", "user"] })
+                ]
             }),
             Components({
-                dirs: ['src/components/', 'src/view/'],
+                dirs: ["src/components/", "src/view/"],
                 include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
                 resolvers: [
                     ElementPlusResolver(),
@@ -93,31 +94,24 @@ export default defineConfig(({ mode }) => {
                     VueUseDirectiveResolver(),
                     IconsResolver({
                         prefix: "tjm-icon",
-                        customCollections: ["ep", "home", "user"],
-                    }),
-                ],
+                        customCollections: ["ep", "home", "user"]
+                    })
+                ]
             }),
             // 静态Icon 插件配置 https://icones.js.org/ https://icones.netlify.app/  vscode插件Iconify IntelliSense
             Icons({
                 compiler: "vue3",
                 customCollections: {
-                    home: FileSystemIconLoader("src/assets/svg/home", (svg) =>
-                        svg.replace(/^<svg /, '<svg fill="currentColor" ')
-                    ),
-                    user: FileSystemIconLoader("src/assets/svg/user", (svg) =>
-                        svg.replace(/^<svg /, '<svg fill="currentColor" ')
-                    ),
+                    home: FileSystemIconLoader("src/assets/svg/home", (svg) => svg.replace(/^<svg /, '<svg fill="currentColor" ')),
+                    user: FileSystemIconLoader("src/assets/svg/user", (svg) => svg.replace(/^<svg /, '<svg fill="currentColor" '))
                 },
-                autoInstall: true,
+                autoInstall: true
             }),
             createSvgIconsPlugin({
-                iconDirs: [path.resolve(process.cwd(), 'src/assets/svg')],
-                symbolId: 'icon-[dir]-[name]',
+                iconDirs: [path.resolve(process.cwd(), "src/assets/svg")],
+                symbolId: "icon-[dir]-[name]",
                 svgoOptions: true
             })
-        ],
-
+        ]
     }
-}
-
-)
+});