Commit 70bcd331 by lqi

feature(custom): init

parents
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 11:21:32
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-02-22 11:21:48
* @FilePath: /review-front/.cz-config.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
module.exports = {
types: [
{ value: 'feature', name: 'feature: 增加新功能' },
{ value: 'bug', name: 'bug: 测试反馈bug列表中的bug号' },
{ value: 'fix', name: 'fix: 修复bug' },
{ value: 'ui', name: 'ui: 更新UI' },
{ value: 'docs', name: 'docs: 文档变更' },
{ value: 'style', name: 'style: 代码格式(不影响代码运行的变动)' },
{ value: 'perf', name: 'perf: 性能优化' },
{ value: 'refactor', name: 'refactor: 重构(既不是增加feature,也不是修复bug)' },
{ value: 'release', name: 'release: 发布' },
{ value: 'deploy', name: 'deploy: 部署' },
{ value: 'test', name: 'test: 增加测试' },
{ value: 'chore', name: 'chore: 构建过程或辅助工具的变动(更改配置文件)' },
{ value: 'revert', name: 'revert: 回退' },
{ value: 'build', name: 'build: 打包' }
],
// override the messages, defaults are as follows
messages: {
type: '请选择提交类型:',
customScope: '请输入您修改的范围(可选):',
subject: '请简要描述提交 message (必填):',
body: '请输入详细描述(可选,待优化去除,跳过即可):',
footer: '请输入要关闭的issue(待优化去除,跳过即可):',
confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
},
allowCustomScopes: true,
skipQuestions: ['body', 'footer'],
subjectLimit: 72
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 10:45:04
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-02-22 14:31:37
* @FilePath: /review-front/.eslintrc.cjs
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-prettier/skip-formatting'
],
env: {
node: true
},
parserOptions: {
ecmaVersion: 'latest'
},
rules: {
// prettier关注于美观度 eslint关注于规范
// 前置条件
// 1.禁用格式化插件 prettier format on save 关闭
// 2.安装Eslint插件, 并配置保存时自动修复
'prettier/prettier': [
'warn',
{
singleQuote: true, // 单引号
semi: false, // 无分号
printWidth: 100, // 每行宽度至多80字符
trailingComma: 'none', // 不加对象|数组最后逗号
endOfLine: 'auto' // 换行符号不限制(win mac 不一致)
}
],
// 定义组件多单词名称
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index'] // vue组件名称多单词组成(忽略index.vue)
}
],
'vue/no-setup-props-destructure': ['off'], // 关闭 props 解构的校验
// 💡 添加未定义变量错误提示,create-vue@3.6.3 关闭,这里加上是为了支持下一个章节演示。
'no-undef': 'error'
}
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
components.d.ts
.history
*.zip
\ No newline at end of file
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}
\ No newline at end of file
# review-front
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
pnpm install
```
### Compile and Hot-Reload for Development
```sh
pnpm dev
```
### Compile and Minify for Production
```sh
pnpm build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
pnpm test:unit
```
### Lint with [ESLint](https://eslint.org/)
```sh
pnpm lint
```
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 11:18:29
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-02-22 11:18:35
* @FilePath: /review-front/commitlint.config.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
module.exports = {
extends: ['@commitlint/config-conventional', 'cz'],
rules: {
'type-enum': [
2,
'always',
[
'feature', // 新功能(feature)
'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况
'fix', // 修补bug
'ui', // 更新 ui
'docs', // 文档(documentation)
'style', // 格式(不影响代码运行的变动)
'perf', // 性能优化
'release', // 发布
'deploy', // 部署
'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
'test', // 增加测试
'chore', // 构建过程或辅助工具的变动
'revert', // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)
'merge', // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址
'build' // 打包
]
],
// <type> 格式 小写
'type-case': [2, 'always', 'lower-case'],
// <type> 不能为空
'type-empty': [2, 'never'],
// <scope> 范围不能为空
'scope-empty': [2, 'never'],
// <scope> 范围格式
'scope-case': [0],
// <subject> 主要 message 不能为空
'subject-empty': [2, 'never'],
// <subject> 以什么为结束标志,禁用
'subject-full-stop': [0, 'never'],
// <subject> 格式,禁用
'subject-case': [0, 'never'],
// <body> 以空行开头
'body-leading-blank': [1, 'always'],
'header-max-length': [0, 'always', 72]
}
}
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 10:45:04
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-20 09:59:08
* @FilePath: /review-front/index.html
* @Description:
-->
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>专业技术人才评审系统 V2.0</title>
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
</head>
<body>
<div id="app">
</div>
<script type="module" src="/src/main.js"></script>
<script>
window.voteDelay = [0, 200]
window.multipleVoteDelay = [50, 200]
</script>
</body>
</html>
\ No newline at end of file
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
<style>
.cube-wrapper {
position: fixed;
top: 0;
left: 0;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
/* color: var(--rainbow-color); */
/* background-color: #81B0FD; */
user-select: none;
animation: position-flow 10s linear infinite;
background-size: 300% 300%;
background-image: linear-gradient(-225deg, #5D9FFF 0%, #B8DCFF 48%, #6BBBFF 100%);
}
/* 背景颜色渐变动画 */
@keyframes color-flow {
0% {
background-color: rgba(132, 180, 251); /* 浅蓝色 */
}
25% {
background-color: #f0f5ff; /* 浅绿色 */
}
50% {
background-color: #bae7ff; /* 深绿色 */
}
75% {
background-color: #f0f5ff; /* 再次回到蓝色 */
}
100% {
background-color: rgba(132, 180, 251); /* 淡绿色 */
}
}
/* 背景位置流动动画 */
@keyframes position-flow {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.cube-folding {
width: 50px;
height: 50px;
display: inline-block;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
transform: rotate(45deg);
font-size: 0;
margin-bottom: 30px;
}
.cube-folding span {
position: relative;
width: 25px;
height: 25px;
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
transform: scale(1.1);
display: inline-block;
}
.cube-folding span::before {
content: '';
background-color: #e6f4ff;
position: absolute;
left: 0;
top: 0;
display: block;
width: 25px;
height: 25px;
-moz-transform-origin: 100% 100%;
-ms-transform-origin: 100% 100%;
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
-webkit-animation: folding 2.5s infinite linear both;
-moz-animation: folding 2.5s infinite linear both;
animation: folding 2.5s infinite linear both;
}
.cube-folding .leaf2 {
-webkit-transform: rotateZ(90deg) scale(1.1);
-moz-transform: rotateZ(90deg) scale(1.1);
transform: rotateZ(90deg) scale(1.1);
}
.cube-folding .leaf2::before {
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s;
background-color: #91caff;
}
.cube-folding .leaf3 {
-webkit-transform: rotateZ(270deg) scale(1.1);
-moz-transform: rotateZ(270deg) scale(1.1);
transform: rotateZ(270deg) scale(1.1);
}
.cube-folding .leaf3::before {
-webkit-animation-delay: 0.9s;
animation-delay: 0.9s;
background-color: #4096ff;
}
.cube-folding .leaf4 {
-webkit-transform: rotateZ(180deg) scale(1.1);
-moz-transform: rotateZ(180deg) scale(1.1);
transform: rotateZ(180deg) scale(1.1);
}
.cube-folding .leaf4::before {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
background-color: #69b1ff;
}
@-webkit-keyframes folding {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
-moz-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
-moz-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
-webkit-opacity: 1;
-moz-opacity: 1;
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
-moz-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
}
@-moz-keyframes folding {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
-moz-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
-moz-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
-webkit-opacity: 1;
-moz-opacity: 1;
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
-moz-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
}
@-ms-keyframes folding {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
-moz-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
-moz-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
-webkit-opacity: 1;
-moz-opacity: 1;
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
-moz-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
}
@keyframes folding {
0%,
10% {
-webkit-transform: perspective(140px) rotateX(-180deg);
-moz-transform: perspective(140px) rotateX(-180deg);
transform: perspective(140px) rotateX(-180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
25%,
75% {
-webkit-transform: perspective(140px) rotateX(0deg);
-moz-transform: perspective(140px) rotateX(0deg);
transform: perspective(140px) rotateX(0deg);
-webkit-opacity: 1;
-moz-opacity: 1;
opacity: 1;
}
90%,
100% {
-webkit-transform: perspective(140px) rotateY(180deg);
-moz-transform: perspective(140px) rotateY(180deg);
transform: perspective(140px) rotateY(180deg);
-webkit-opacity: 0;
-moz-opacity: 0;
opacity: 0;
}
}
.cube-wrapper:after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -20px;
margin: auto;
width: 90px;
height: 6px;
z-index: 1;
background-color: rgba(0, 0, 0, 0.1);
-webkit-filter: blur(2px);
filter: blur(2px);
-webkit-border-radius: 100%;
-moz-border-radius: 100%;
border-radius: 100%;
-webkit-animation: shadow 0.5s ease infinite alternate;
-moz-animation: shadow 0.5s ease infinite alternate;
animation: shadow 0.5s ease infinite alternate;
}
.cube-wrapper .loading {
font-size: 12px;
letter-spacing: 0.1em;
display: block;
color: #fff;
position: relative;
font-weight: 600;
top: 25px;
z-index: 2;
-webkit-animation: text 0.5s ease infinite alternate;
-moz-animation: text 0.5s ease infinite alternate;
animation: text 0.5s ease infinite alternate;
}
@-webkit-keyframes text {
100% {
top: 15px;
}
}
@-moz-keyframes text {
100% {
top: 15px;
}
}
@-ms-keyframes text {
100% {
top: 15px;
}
}
@keyframes text {
100% {
top: 15px;
}
}
@-webkit-keyframes shadow {
100% {
bottom: -18px;
width: 100px;
}
}
@-moz-keyframes shadow {
100% {
bottom: -18px;
width: 100px;
}
}
@-ms-keyframes shadow {
100% {
bottom: -18px;
width: 100px;
}
}
@keyframes shadow {
100% {
bottom: -18px;
width: 100px;
}
}
</style>
<div class="cube-wrapper">
<div class="cube-folding">
<span class="leaf1"></span>
<span class="leaf2"></span>
<span class="leaf3"></span>
<span class="leaf4"></span>
</div>
<span class="loading" data-name="Loading">Loading</span>
</div>
\ No newline at end of file
{
"name": "review-front",
"version": "2.1.4",
"private": true,
"type": "commonjs",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test:unit": "vitest",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/",
"commit": "git-cz"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@antv/g2": "^5.2.4",
"@vueuse/core": "^10.9.0",
"ant-design-vue": "^4.1.2",
"axios": "^1.6.7",
"clipboard": "^2.0.11",
"dayjs": "^1.11.10",
"js-cookie": "^3.0.5",
"lodash-es": "^4.17.21",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"splitpanes": "^3.1.5",
"vue": "^3.4.15",
"vue-router": "^4.2.5",
"vue-signature-pad": "^3.0.2"
},
"devDependencies": {
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.2",
"@rushstack/eslint-patch": "^1.3.3",
"@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/test-utils": "^2.4.4",
"commitizen": "^4.3.0",
"commitlint-config-cz": "^0.13.3",
"consola": "^3.2.3",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^7.0.0",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"husky": "7.0.4",
"jsdom": "^24.0.0",
"lint-staged": "^15.2.2",
"prettier": "^3.0.3",
"rollup-plugin-external-globals": "^0.12.0",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.71.1",
"terser": "^5.34.1",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.11",
"vite-plugin-app-loading": "^0.3.0",
"vite-plugin-compression2": "^1.3.0",
"vite-plugin-vue-devtools": "^7.5.3",
"vitest": "^1.2.2"
},
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
},
"lint-staged": {
"*.{js,ts}": [
"npm run lint",
"npm run format"
]
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 10:45:04
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-11 15:05:30
* @FilePath: /review-front/src/App.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup>
import { reactive, ref, computed, onMounted, getCurrentInstance, watchEffect } from 'vue'
import { RouterView } from 'vue-router'
import { useUserStoreHook } from '@/stores/modules/user'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
import { setExpertInfo } from '@/api/user/info'
const locale = zhCN
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
dayjs.locale('zh-cn')
const noName = reactive({
open: false,
confirmLoading: false,
form: {
userName: ''
}
})
const { proxy } = getCurrentInstance()
const useUserStore = useUserStoreHook()
const formRef = ref(null)
const userName = computed(() => useUserStore.userName)
watchEffect(() => {
if (useUserStore.token && !userName.value) {
noName.open = true
}
})
onMounted(() => {})
const handleOk = () => {
console.log(formRef.value)
formRef.value.validate().then(async () => {
try {
noName.confirmLoading = true
const res = await setExpertInfo({
id: useUserStore.expertUser.id,
userName: noName.form.userName
})
if (res.code === 200) {
proxy.$message.success(res.message)
useUserStore.setUserName(noName.form.userName)
noName.open = false
}
} catch (error) {
throw new Error(error)
} finally {
setTimeout(() => {
noName.confirmLoading = false
}, 500)
}
console.log('sub')
})
}
const componentSize = computed(() => {
let size = 'middle'
const w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
if (w <= 1366) {
size = 'small'
}
return size
})
</script>
<template>
<a-config-provider :locale="locale" :component-size="componentSize">
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
<a-modal
v-model:open="noName.open"
title="录入专家姓名"
:maskClosable="false"
:keyboard="false"
:destroyOnClose="true"
:closable="false"
@ok="handleOk"
>
<a-form ref="formRef" :model="noName.form" autocomplete="off" style="margin: 2rem 0">
<a-form-item
label="姓名"
name="userName"
:rules="[{ required: true, message: '请录入您的真实姓名!' }]"
>
<a-input v-model:value="noName.form.userName"></a-input>
</a-form-item>
</a-form>
<template #footer>
<a-button :loading="noName.confirmLoading" type="primary" @click="handleOk"
>确定无误,保存</a-button
>
</template>
</a-modal>
</a-config-provider>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
import request from '@/utils/request'
/**
* @description: 获取分组信息列表
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function fetchList(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/adminPage`,
method: 'post',
data
})
}
export function getGroupListByPlan(planId, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/getGroupByPlanId/${planId}`,
method: 'get'
})
}
/**
* @description: 创建
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function createObj(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/adminAdd`,
method: 'post',
data
})
}
/**
* @description: 删除
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function deleteObj(id, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/adminDelete/${id}`
})
}
/**
* @description: 修改
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function updateObj(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/adminModify`,
method: 'post',
data
})
}
/**
* @description: 修改状态
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function setStatusObj(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/setStatus`,
method: 'post',
data
})
}
/**
* @description: 获取打分模板
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function getScoreTemplate(unitId) {
return request({
url: `/api-evaluation/evaluation/scoringTemplate/adminGetOptions${unitId ? '/' + unitId : ''}`,
method: 'get'
})
}
/**
* @description: 创建缓存
* @param {*} planId
* @param {*} groupId
* @return {*}
* @author: Lqi
*/
export function createCache(planId, groupId, type) {
return request({
url: `/api-evaluation/api${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/expert/createRedisAndVoteByPlanIdAndGroupId/${planId}/${groupId}`,
method: 'get'
})
}
/**
* @description: 清除缓存
* @param {*} planId
* @param {*} groupId
* @return {*}
* @author: Lqi
*/
export function deleteCache(planId, groupId, type) {
return request({
url: `/api-evaluation/api${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/expert/clearVoteRedisByPlanIdAndGroupId/${planId}/${groupId}`,
method: 'get'
})
}
/**
* @description: 清除数据库投票数据
* @param {*} planId
* @param {*} groupId
* @return {*}
* @author: Lqi
*/
export function deleteDataBaseVotes(planId, groupId, type) {
return request({
url: `/api-evaluation/api${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/expert/clearVoteByPlanIdAndGroupId/${planId}/${groupId}`,
method: 'get'
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-12-18 14:58:28
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-18 15:46:28
* @FilePath: /review-front/src/api/admin/personnel.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '@/utils/request'
/**
* @description: 获取分组信息列表
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function fetchList(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/personnel/adminPage`,
method: 'post',
data
})
}
export function adminTaskPage(data, type) {
return request({
url: `/api-review/activiti/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/adminTaskPage`,
method: 'post',
data
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-12-12 16:18:59
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-17 09:33:49
* @FilePath: /review-front/src/api/admin/plan.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '@/utils/request'
/**
* @description: 获取列表
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function fetchList(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/plan/adminPage`,
method: 'post',
data
})
}
/**
* @description: 设置状态
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function setStatusObj(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/plan/adminSetStatus`,
method: 'post',
data
})
}
/**
* @description: 创建
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function createObj(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/plan/adminAdd`,
method: 'post',
data
})
}
/**
* @description: 更新
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function updateObj(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/plan/adminModify`,
method: 'post',
data
})
}
/**
* @description: 删除
* @param {*} id
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function deleteObj(id, type) {
return request({
url:
`/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/plan/adminDelete/` + id,
method: 'get'
})
}
/**
* @description: 获取公共评委会附件列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getCommonUploadFileList(data) {
return request({
url: '/api-evaluation/file/getCommonUploadFileList',
method: 'post',
data
})
}
/**
* @description: 获取教育评委会附件列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getEduUploadFileList(data) {
return request({
url: '/api-evaluation/file/getEduUploadFileList',
method: 'post',
data
})
}
/**
* @description: 获取卫生评委会附件列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getHOUploadFileList(data) {
return request({
url: '/api-evaluation/file/getHOUploadFileList',
method: 'post',
data
})
}
/**
* @description: 删除评委会附件列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function deleteEvaluationFile(data) {
return request({
url: '/api-evaluation/file/deleteEvaluationFile',
method: 'post',
data
})
}
/**
* @description: 获取计划列表
* @param {*} unitId
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function getPlanList(unitId, type) {
return request({
url:
`/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/plan/adminGetPlan/` + unitId,
method: 'get'
})
}
import request from '@/utils/request'
/**
* @description: 获取评委会信息
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getJuryList(data) {
return request({
url: '/api-system/unitEvaluationPower/page',
method: 'post',
data
})
}
import request from '@/utils/request'
/**
* @description:评审展示图形显示-通过personelId查询参评
* @param {*} personelId
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function getPersonelReviewDetail(personelId, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/personnel/getReviewResultRedis/${personelId}`,
method: 'get'
})
}
/**
* @description: 评审展示图形显示-获取会场情况数据专家实到人数-评审专家法定票数-参评总人数-参评未评人数-参评已评人数
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function getReviewDetail(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/personnel/getReviewShowFromRedis`,
method: 'post',
data
})
}
/**
* @description: 评审展示图形显示-参评人员评审情况查询
* @param {*} data
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function getResultList(data, type) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/personnel/resultListFromRedis`,
method: 'post',
data
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-23 14:44:10
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-02-23 16:49:45
* @FilePath: /review-front/src/api/dict.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '@/utils/request'
/**
* @description: 获取字典
* @param {*} key
* @return {*}
* @author: Lqi
*/
export function fetchDataList(key) {
return request({
url: '/api-system/dict/getDictListByName',
method: 'post',
data: key
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-23 14:24:08
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-11 14:50:56
* @FilePath: /review-front/src/api/login.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '@/utils/request'
/**
* @description:
* @param {*} data
* @param {*} type 类型(专家/观察员)
* @return {*}
* @author: Lqi
*/
export function loginByUsername(data, type) {
return request({
url: `/api-evaluation/api/auth/${type}Login`,
method: 'post',
data
})
}
/**
* @description: 退出登录
* @param {*} type类型(专家/观察员)
* @return {*}
* @author: Lqi
*/
export function logout(type) {
return request({
url: '/api-evaluation/api/auth/' + type + 'Logout',
method: 'get'
})
}
/**
* @description: 管理员登录
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function adminLogin(data) {
return request({
url: `/api-evaluation/auth/login`,
method: 'post',
data
})
}
/**
* @description: 管理员退出登录
* @return {*}
* @author: Lqi
*/
export function adminLogout() {
return request({
url: '/api-evaluation/api/auth/logout',
method: 'get'
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-06 15:18:05
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-03-06 15:19:19
* @FilePath: /review-front/src/api/observer.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 观察员
import request from '@/utils/request'
/**
* @description: 观察员获取审核列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function fetchReviewList(data) {
return request({
url: '/api-evaluation/api/observer/reviewList',
method: 'get',
data
})
}
/**
* @description: 获取观察员所在计划和组的专家列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function fetchExpertList(data) {
return request({
url: '/api-evaluation/api/observer/getExpertList',
method: 'post',
data
})
}
/**
* @description: 设置主审
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function setMainReview(data) {
return request({
url: '/api-evaluation/api/observer/setMainReview',
method: 'post',
data
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-06 17:00:49
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-12 15:00:44
* @FilePath: /review-front/src/api/details.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 详情页
import request from '@/utils/request'
import { useAppStoreHook } from '@/stores/modules/app'
/**
* @description: 获取申报人详细信息
* @param {*} id
* @param {*} planId
* @param {*} groupId
* @return {*}
* @author: Lqi
*/
export function getReviewDetail(id, planId, groupId) {
return request({
url: `/api-evaluation/${
useAppStoreHook().baseAPI
}/expert/expertUser/reviewListDataById/${id}/${planId}/${groupId}`,
method: 'get'
})
}
/**
* @description: 获取附件
* @param {*} userId
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function enclosure(userId, year) {
return request({
url: '/api-review/report/attachment/file/list',
handle: { systemId: 2 },
method: 'post',
params: { year, userId }
})
}
/**
* @description: 获取专家意见
* @return {*}
* @author: Lqi
*/
export function getOpinion(personalId) {
return request({
url:
'/api-evaluation/' +
useAppStoreHook().baseAPI +
'/expert/getExpertUserReviewOpinion/' +
personalId,
method: 'get'
})
}
/**
* @description: 设置专家意见
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function setOpinion(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/expertUserSetMainOpinion',
method: 'post',
data
})
}
/**
* @description: 专家打分
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function setScore(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/expertUserScoreReview',
method: 'post',
data
})
}
/**
* @description: 辅审同意意见
* @param {*} personalId
* @return {*}
* @author: Lqi
*/
export function auxiliarySetAgree(personalId) {
return request({
url:
'/api-evaluation/' +
useAppStoreHook().baseAPI +
'/expert/expertUserSetAuxiliaryOpinion/' +
personalId,
method: 'get'
})
}
/**
* @description: 获取打分信息
* @param {*} id
* @return {*}
* @author: Lqi
*/
export function getScoreInfo(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/getExpertUserScoreInfo',
method: 'post',
data
})
}
/**
* @description: 获取主审打分信息
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getMainScoreInfo(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/getExpertUserMainScoreInfo',
method: 'post',
data
})
}
/**
* @description: 获取打分模板和结果
* @param {*} personnelId
* @param {*} groupId
* @return {*}
* @author: Lqi
*/
export function getTemplateTreeResult(personnelId, groupId) {
return request({
url: `/api-evaluation/api/template/expertUserGetTemplateTreeAndEvaluationResult/${personnelId}/${groupId}`
})
}
/**
* @description: 获取打分结果
* @param {*} personnelId
* @param {*} groupId
* @return {*}
* @author: Lqi
*/
export function getTemplateTreeScoreResult(type, personnelId, groupId) {
return request({
url: `/api-evaluation/api${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/template/expertUserGetTemplateTreeScore/${personnelId}/${groupId}`
})
}
/**
* @description: 获取会议文件列表
* @param {*} type
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getReviewFileList(type, data) {
let urlStr =
type === 'EDU'
? 'getEduUploadFileList'
: type === 'COMMON'
? 'getCommonUploadFileList'
: 'getHOUploadFileList'
return request({
url: `/api-evaluation/file/${urlStr}`,
method: 'post',
data
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-10-31 17:28:53
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-23 14:15:23
* @FilePath: /review-front/src/api/ho.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/**
* @description: 教育特殊表 请求接口
* @return {*}
* @author: Lqi
*/
import request from '@/utils/request'
/**
* @description: 获取抗疫一线信息
* @param {*} id
* @return {*}
* @author: Lqi
*/
export function getScoreExtendByUserId(id) {
return request({
url: '/api-review/reportScoreExtend/getInfoByUserId/' + id,
method: 'get'
})
}
export function getScoreExtendByReportId(id) {
return request({
url: '/api-review/reportScoreExtend/getInfoByReportId/' + id,
method: 'get'
})
}
/**
* @description: 获取考试成绩
* @param {*} id
* @return {*}
* @author: Lqi
*/
// /api-review/personal/report/getAdvancedHygieneExamScore/202373
export function getExamScoreByUserId(id) {
return request({
url: '/api-evaluation/personal/report/getAdvancedHygieneExamScore/' + id,
method: 'get'
})
}
/**
* @description: 历史申报
* @param {*} id
* @return {*}
* @author: Lqi
*/
export function getRankSituationByUserId(id) {
return request({
url: '/api-evaluation/personal/report/getAdvancedHygieneRankSituation/' + id,
method: 'get'
})
}
/**
* @description: 历史申报
* @param {*} id
* @return {*}
* @author: Lqi
*/
export function getWorkloadByUserId(id) {
return request({
url: '/api-evaluation/advancedHygieneWorkload/getWorkloadByReportId/' + id,
method: 'get'
})
}
/**
* @description: DRG数据
* @param {*} id
* @return {*}
* @author: Lqi
*/
export function getDRGInfo(id) {
return request({
url: '/api-evaluation/evaluation-ho/personnel/drg/' + id,
method: 'get'
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-06 15:01:02
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-11 14:53:02
* @FilePath: /review-front/src/api/info.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '@/utils/request'
/**
* @description: 获取专家基本信息
* @return {*}
* @author: Lqi
*/
export function getExpertInfo() {
return JSON.parse(localStorage.getItem('user'))
}
/**
* @description: 专家设置基本信息
* @return {*}
* @author: Lqi
*/
export function setExpertInfo(data) {
return request({
url: '/api-evaluation/evaluation/expertUser/updateExpertUserAndInfo',
method: 'post',
data
})
}
/**
* @description: 专家获取评审记录
* @return {*}
* @author: Lqi
*/
export function getReviewReconds() {}
/**
* @description: 专家登录完获取配置列表
* @return {*}
* @author: Lqi
*/
export function getConfigList() {
return request({
url: '/api-evaluation/evaluation/expertPlanGroupConf/getExpertPlanGroupConfList',
method: 'get'
})
}
/**
* @description: 查询单位
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getUnitList(data) {
return request({
url: '/api-system/unit/common/findUnitPage',
method: 'post',
data
})
}
/**
* @description: 获取专家信息
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getExpert(data) {
return request({
url: '/api-evaluation/evaluation/expertUserInfo/selectExperUserInfo',
method: 'post',
data
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-05 14:38:42
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-04 11:32:21
* @FilePath: /review-front/src/api/reportForm.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/**
* @description: 申报表API
* @return {*}
* @author: Lqi
*/
import request from '@/utils/request'
/**
* @description: 获取申报年份
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getYearByReportId(data) {
return request({
url: '/api-review/report/review/queryYearByReportId',
method: 'post',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: data
})
}
/**
* @description: 获取申报信息
* @param {*} reportId
* @return {*}
* @author: Lqi
*/
export function getReportOrReview(reportId) {
return request({
url: '/api-review/personal/report/getReportOrReview/' + reportId,
method: 'get'
})
}
/**
* @description: 获取基本信息
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getBasicInfo(id, year) {
return request({
url: '/api-review/personal/report/getBasicInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取年度考核信息
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getCheckedInfo(id, year) {
return request({
url: '/api-review/personal/report/getCheckedInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取学历信息
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getEduInfo(id, year) {
return request({
url: '/api-review/personal/report/getEduInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取完成工作情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getFinishedInfo(id, year) {
return request({
url: '/api-review/personal/report/getFinishedInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取继续教育和培训情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getLearnInfo(id, year) {
return request({
url: '/api-review/personal/report/getLearnInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取专利情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getPatentInfo(id, year) {
return request({
url: '/api-review/personal/report/getPatentInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取完成项目情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getProjectInfo(id, year) {
return request({
url: '/api-review/personal/report/getProjectInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取职称信息
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getRankInfo(id, year) {
return request({
url: '/api-review/personal/report/getRankInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取完成工作总结
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getResultInfo(id, year) {
return request({
url: '/api-review/personal/report/getResultInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取表彰奖励情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getRewardInfo(id, year) {
return request({
url: '/api-review/personal/report/getRewardInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取著作论文情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getThesisInfo(id, year) {
return request({
url: '/api-review/personal/report/getThesisInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取学习经历信息
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getStudyInfo(id, year) {
return request({
url: '/api-review/personal/report/getStudyInfo/' + id,
method: 'get',
params: { year }
})
}
/**
* @description: 获取工作情况
* @param {*} id
* @param {*} year
* @return {*}
* @author: Lqi
*/
export function getWorkInfo(id, year) {
return request({
url: '/api-review/personal/report/getWorkInfo/' + id,
method: 'get',
params: { year }
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-05 11:18:35
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-15 09:28:28
* @FilePath: /review-front/src/api/reviewList.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 评审列表请求API
import request from '@/utils/request'
import { useAppStoreHook } from '@/stores/modules/app'
/**
* @description: 获取专家列表(公用)
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function fetchReviewList(data) {
// console.log('APIENUM', APIENUM)
// console.log('baseAPI', JSON.parse(localStorage.getItem('plan'))?.planInfo.reviewType)
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/expertUserReviewList',
method: 'post',
data
})
}
/**
* @description: 获取所有待评人员ID
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getReviewAllIds(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/expertUserReviewListAll',
method: 'post',
data
})
}
/**
* @description: 组长查询评审列表(调整主辅审)
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function fetchReviewListPc(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/reviewListPc',
method: 'post',
data
})
}
/**
* @description: 投票接口
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function setVote(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/expert/expertUserVoteReview',
method: 'post',
data
})
}
/**
* @description: 获取分组信息
* @param {*} planId
* @param {*} type
* @return {*}
* @author: Lqi
*/
export function getGroupInfo(type, planId, groupId) {
return request({
url: `/api-evaluation/evaluation${
type && type !== 'COMMON' ? '-' + type.toLowerCase() : ''
}/group/adminGetGroup/${planId}/${groupId}`,
method: 'get'
})
}
/**
* @description: 权限查看列表
* @return {*}
* @author: Lqi
*/
export function getFileViewPowerList(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/view/power/expertUserShowViewPowerlist',
method: 'post',
data
})
}
/**
* @description: 专家设置查看材料权限
* @return {*}
* @author: Lqi
*/
export function setFileViewPowerByExpert(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/view/power/setExpertUserPower',
method: 'post',
data
})
}
/**
* @description: 查询所有专家列表
* @param {*} data
* @return {*}
* @author: Lqi
*/
export function getExpertsAll(data) {
return request({
url: '/api-evaluation/' + useAppStoreHook().baseAPI + '/view/power/expertUserListAll',
method: 'post',
data
})
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-08-27 10:59:26
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-10 17:19:31
* @FilePath: /review-front/src/api/statistics.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/**
* @description: 统计接口
* @return {*}
* @author: Lqi
*/
import request from '@/utils/request'
import { useAppStoreHook } from '@/stores/modules/app'
export function getExpertStatistics(planId, groupId) {
return request({
url: `/api-evaluation/${
useAppStoreHook().baseAPI
}/personnel/getExpertUser/statistics/${planId}/${groupId}`,
method: 'get'
})
}
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
html {
font-size: 16px;
}
body {
color: var(--color-text);
// background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.ant-modal div[aria-hidden='true'] {
display: none !important;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
@import './base.scss';
html,
body,
#app {
margin: 0;
padding: 0;
height: 100%;
}
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.flex-c {
justify-content: center;
align-items: center;
}
.flex-between {
justify-content: space-between;
}
.flex-c-b {
justify-content: space-between;
align-items: center;
}
.page-wrapper {
width: 100%;
border-radius: 8px;
.form-wrapper {
padding: 1rem 0;
}
&.page-bg {
background-color: #fff;
box-shadow:
0 1px 2px 0 rgba(0, 0, 0, 0.03),
0 1px 6px -1px rgba(0, 0, 0, 0.02),
0 2px 4px 0 rgba(0, 0, 0, 0.02);
}
}
.ant-checkbox .ant-checkbox-inner {
border-color: #000;
}
:where(.css-dev-only-do-not-override-1hsjdkk).ant-checkbox-checked .ant-checkbox-inner {
border-color: #1677ff;
}
.login-wapper {
position: relative;
height: 100%;
background-image: url('./login-bg.jpg');
background-color: #fff;
background-repeat: no-repeat;
background-position: center;
background-attachment: fixed;
transition: background-image 1s ease-in-out;
.login-form {
background-color: #fff;
padding: 3rem 2rem;
h2 {
font-weight: bold;
// margin-bottom: 0.5rem;
font-size: 28px;
}
h4 {
font-weight: bold;
font-size: 24px;
margin-bottom: 1.5rem;
color: #1677ff;
}
}
.decorate {
width: 300px;
height: 300px;
border-radius: 50%;
background: radial-gradient(
circle,
rgba(#5990ff, 1) 40%,
rgba(#68a5fe, 0.8) 60%,
rgba(#84b4fb, 0.6) 70%,
rgba(#fff, 0.3) 90%,
rgba(#fff, 0) 95%
);
box-shadow: 0 0 2px 2px rgba(#80b3ff, 0.2);
animation: float 8s ease-in-out infinite;
}
@keyframes float {
0% {
transform: translateY(0);
opacity: 0.8;
}
50% {
transform: translateY(-40px);
opacity: 1;
}
100% {
transform: translateY(0);
opacity: 0.8;
}
}
.copyright {
position: absolute;
bottom: 10px;
font-size: 12px;
color: #8c8c8c;
}
}
.datas {
margin-top: 16px;
}
\ No newline at end of file
@media screen and (max-width: 1366px) {
.ant-table-wrapper {
.ant-table {
th.ant-table-cell {
line-height: 1 !important;
}
}
}
.app-wrapper.ant-layout {
height: 100%;
.header {
height: 3rem;
}
}
}
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-11 11:45:19
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-09-11 09:55:47
* @FilePath: /review-front/src/components/Card/ColorCard.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="color-card" :style="{ backgroundImage: `linear-gradient(${[color[0], color[1]]})` }">
<slot></slot>
</div>
</template>
<script setup>
defineProps({
color: Array
})
</script>
<style lang="scss" scoped>
.color-card {
height: 12.5rem;
width: 100%;
// min-width: 20rem;
// max-width: 24rem;
border-radius: 0.5rem;
position: relative;
overflow: hidden;
z-index: 1;
&::before {
content: '';
display: block;
width: 14rem;
height: 14rem;
position: absolute;
top: -70%;
right: -3rem;
background-color: rgba(#fff, 0.1);
transform: rotate(45deg);
border-radius: 3rem;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1);
z-index: 0;
}
&::after {
content: '';
display: block;
width: 10rem;
height: 10rem;
position: absolute;
top: -50%;
right: -1rem;
background-color: rgba(#fff, 0.1);
transform: translateY(50%);
transform: rotate(45deg);
border-radius: 3rem;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1);
z-index: 0;
}
}
</style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-11 11:29:42
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-30 15:34:37
* @FilePath: /review-front/src/components/Card/Card.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="i-card">
<header class="i-crad-title">
<slot name="title"> </slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script setup></script>
<style lang="scss">
.i-card {
background-color: #fff;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
.i-crad-title {
font-weight: bolder;
h2 {
font-size: 1.25rem;
}
}
}
</style>
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-11 11:45:07
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-03-11 15:50:48
* @FilePath: /review-front/src/components/Card/index.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import ColorCard from './ColorCard.vue'
import Card from './CusCard.vue'
export { ColorCard, Card }
<template>
<div class="get-fail">
<div class="fail-content">
<Spin
:spinning="useReport.reloadLoading.value"
tip="数据加载中..."
size="large"
style="margin-bottom: 1rem"
>
</Spin>
<br />
<p v-show="!useReport.reloadLoading.value" class="tips">
{{ text }}数据获取失败,请重新获取。
</p>
<a-button :loading="useReport.reloadLoading.value" type="primary" ghost @click="reload"
>重新获取</a-button
>
</div>
</div>
</template>
<script setup>
import { defineEmits, defineProps } from 'vue'
import { Spin } from 'ant-design-vue'
import { useReportForm } from '@/hooks/useReportForm'
const useReport = useReportForm()
const emits = defineEmits(['handleReload'])
defineProps({
text: String
})
const reload = () => {
emits('handleReload')
}
</script>
<style lang="scss" scoped></style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-15 10:02:42
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-05 14:50:01
* @FilePath: /review-front/src/components/Details/Detail.vue
* @Description: 分屏组件
-->
<template>
<div class="split-screen-wrapper">
<div class="split-screen-toolbar"></div>
<Splitpanes class="default-theme">
<Pane
v-if="leftTabsList.length > 0"
:max-size="rightTabsList.length > 0 ? '70' : '100'"
id="left-panel"
>
<a-tabs v-model:activeKey="leftActiveKey" type="card" @change="handleLeftTabsChange">
<a-tab-pane
v-for="item in leftTabsList"
:disabled="useDataStore.fetchLoading || useDataStore.annexLoading"
:key="item.key"
:tab="item.tab"
>
<ReportForm ref="reportForm" v-if="item.name === 'reportForm'" />
<Annex ref="annex" v-if="item.name === 'annex'"></Annex>
<HoSpecialTable
ref="hoSpecialTable"
v-if="item.name === 'hoSpecialTable'"
></HoSpecialTable>
</a-tab-pane>
</a-tabs>
</Pane>
<Pane v-if="rightTabsList.length > 0" :max-size="leftTabsList.length > 0 ? '60' : '100'">
<a-tabs v-model:activeKey="rightActiveKey" type="card">
<a-tab-pane
v-for="item in rightTabsList"
:disabled="useDataStore.fetchLoading || useDataStore.annexLoading"
:key="item.key"
:tab="item.tab"
>
<Opinion v-if="item.name === 'opinion'"></Opinion>
<Score ref="TenScore" v-if="item.name === 'scoring'"></Score>
<Vote ref="vote" v-if="item.name === 'vote'"></Vote>
</a-tab-pane>
</a-tabs>
</Pane>
</Splitpanes>
</div>
</template>
<script setup>
import { ref, computed, reactive, watch, nextTick } from 'vue'
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
// import { useDetailHook } from '@/hooks/useDetail'
// import { useReportForm } from '@/hooks/useReportForm'
import { useDataStoreHook } from '@/stores/modules/data'
import ReportForm from './ReportForm/ReportForm.vue'
import Annex from './reportAnnex.vue'
import HoSpecialTable from './tab/hoSpecialTable.vue'
import Opinion from './reviewOpinion.vue'
import Score from './score/tenPoints.vue'
import Vote from './vote/index.vue'
import { usePlanStoreHook } from '@/stores/modules/plan'
import { useAppStoreHook } from '@/stores/modules/app'
import { EventBus } from '@/components/Details/bus'
import { useReportForm } from '@/hooks/useReportForm'
const useDataStore = useDataStoreHook()
const usePlanStore = usePlanStoreHook()
const useAppStore = useAppStoreHook()
const useReportF = useReportForm()
const reportForm = ref(null)
const annex = ref(null)
const TenScore = ref(null)
const hoSpecialTable = ref(null)
const rightActiveKey = ref(EventBus.rightCurrentKey)
const leftTabs = reactive({
COMMON: [
{ key: '1', tab: '申报表', name: 'reportForm' },
{ key: '2', tab: '附件材料', name: 'annex' }
],
EDU: [
{ key: '1', tab: '申报表', name: 'reportForm' },
{ key: '2', tab: '附件材料', name: 'annex' }
],
HO: [
{ key: '1', tab: '申报表', name: 'reportForm' },
{ key: '2', tab: '附件材料', name: 'annex' },
{ key: '3', tab: '特殊表', name: 'hoSpecialTable' }
]
})
const leftActiveKey = computed({
get() {
return EventBus.leftCurrentKey
},
set(val) {
EventBus.leftCurrentKey = val
}
})
watch(
() => useDataStore.reportId,
async (val) => {
if (val) {
if (leftActiveKey.value === '1') {
nextTick(async () => {
await useReportF.loadData()
})
} else if (leftActiveKey.value === '2') {
nextTick(async () => {
await useDataStore.getAnnex()
})
} else if (leftActiveKey.value === '3') {
nextTick(async () => {
await useDataStore.loadHoSpecialTable()
})
}
}
}
)
const handleLeftTabsChange = async (val) => {
EventBus.leftCurrentKey = val
const dom = document.querySelector('#reportForm')
const dom2 = document.querySelector('#annexWrap')
if (dom) {
dom.scrollTop = 0
} else if (dom2) {
dom2.scrollTop = 0
}
if (val === '1' && (await isRequestReportForm())) {
useReportF.loadData()
}
if (val === '2' && (await isRequestAnnex())) {
useDataStore.getAnnex()
}
if (val === '3' && (await isRequestHoSpecial())) {
useDataStore.loadHoSpecialTable()
}
}
const isRequestReportForm = () => {
let flag = false
if (
!useDataStore.reportForm.id.data ||
useDataStore.personalDetail.detail.detail.reportId !== useDataStore.reportForm.id.data
) {
flag = true
}
return flag
}
const isRequestAnnex = () => {
let flag = false
if (
!useDataStore.annex.id ||
useDataStore.personalDetail.detail.detail.userId !== useDataStore.annex.id
) {
flag = true
}
return flag
}
const isRequestHoSpecial = () => {
let flag = false
if (
!useDataStore.hoSpecial.userId ||
useDataStore.personalDetail.detail.detail.userId !== useDataStore.hoSpecial.userId
) {
flag = true
}
return flag
}
/**
* @description: 详情页分屏左边tab显示
* @param {*} computed
* @return {*}
* @author: Lqi
*/
const leftTabsList = computed(() => {
let _tabArr = []
const isShowDetailed = useDataStore.personalDetail.detail?.detail?.isShowDetailed
const isMain = useDataStore.personalDetail.detail?.detail?.isMain
const hasShowDetailPower = useDataStore.personalDetail.detail?.detail?.hasShowDetailPower
const isAuxiliary = useDataStore.personalDetail.detail?.detail?.isAuxiliary
if (
(isShowDetailed === 1 && (isMain || isAuxiliary || hasShowDetailPower)) ||
isShowDetailed === 2
) {
_tabArr = leftTabs[useAppStore.reviewType]
} else {
_tabArr = leftTabs[useAppStore.reviewType].filter((v) => v.key === '1')
}
return _tabArr
})
const opinion = { key: '1', tab: '意见', name: 'opinion' }
const scoring = { key: '2', tab: '打分', name: 'scoring' }
const vote = { key: '3', tab: '表决', name: 'vote' }
const rightTabsList = computed(() => {
let _tabArr = []
const type = usePlanStore.groupInfo.type
const isMain = useDataStore.personalDetail.detail?.detail?.isMain
const isSetMessage = usePlanStore.groupInfo.isSetMessage
if (isSetMessage) {
_tabArr.push(opinion)
}
if (type === 1 || (type === 2 && isMain)) {
_tabArr.push(scoring)
}
if (type === 0) {
_tabArr.push(vote)
}
return _tabArr
})
</script>
<style lang="scss">
.split-screen-wrapper {
height: 100%;
}
.splitpanes.default-theme .splitpanes__pane {
background-color: #fff;
height: 100%;
padding: 0 0.5rem;
&:last-child {
border-left: 1px solid #f5f5f5;
border-right: 1px solid #f5f5f5;
}
}
</style>
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-09-04 15:51:10
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-09-20 15:12:57
* @FilePath: /review-front/src/components/Details/bus.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { reactive } from 'vue'
export const EventBus = reactive({
nextNum: 0,
leftCurrentKey: '1',
rightCurrentKey: '1'
})
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-15 10:21:07
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-03-15 11:05:07
* @FilePath: /review-front/src/components/Details/index.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import SplitScreen from './SplitScreen.vue'
import Details from './reportDetails.vue'
export { SplitScreen, Details }
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-15 11:04:44
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-20 15:32:15
* @FilePath: /review-front/src/components/Details/Details.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<a-modal
ref="details"
v-model:open="modelVisible"
:title="title"
width="100%"
wrap-class-name="full-modal"
:destroyOnClose="true"
:footer="null"
@cancel="handleClose"
>
<SplitScreen v-if="isShowDetailComp" ref="splitScreen"></SplitScreen>
<div class="personal-switch">
<a-space v-if="useDataStore.allIds.ids.length > 0" wrap>
<a-button
:loading="useDataStore.fetchLoading || useDataStore.annexLoading"
:disabled="_index === slideingData.min"
type="primary"
size="small"
ghost
@click="handleLoadReportInfo(0)"
>上一个</a-button
>
<a-button
:loading="useDataStore.fetchLoading || useDataStore.annexLoading"
:disabled="_index === slideingData.max"
type="primary"
size="small"
ghost
@click="handleLoadReportInfo(1)"
>下一个</a-button
>
</a-space>
<a-button
v-else
:loading="useDataStore.allIds.loading"
type="primary"
size="small"
ghost
@click="handleReloadAllIds"
>重新加载</a-button
>
</div>
</a-modal>
</template>
<script setup>
import { computed, onMounted, ref, defineProps, getCurrentInstance, watch } from 'vue'
import SplitScreen from './SplitScreen.vue'
import { useDataStoreHook } from '@/stores/modules/data'
import { useReportForm } from '@/hooks/useReportForm'
import { cloneDeep } from 'lodash-es'
import { EventBus } from './bus'
// import { useAppStoreHook } from '@/stores/modules/app'
const props = defineProps({
slideingData: Object,
visible: Boolean
})
const { proxy } = getCurrentInstance()
const useDataStore = useDataStoreHook()
const useReportF = useReportForm()
// const userInfo = useDataStore.personalDetail?.detail?.detail
// const useAppStore = useAppStoreHook()
onMounted(() => {})
const _index = ref(0)
const splitScreen = ref(null)
const emits = defineEmits(['close', 'update:visible'])
// const visible = ref(false)
const title = computed(() => {
let _str = '详情,加载中...'
let p = useDataStore.personalDetail?.detail?.detail
if (p) {
_str = `${p.number}-${p.isMain ? '主审 - ' : p.isAuxiliary ? '辅审 - ' : ''}${p.userName} - ${
p.unitName
} - ${proxy.$filters.localDictFilter(
'dictJobTitle',
p.reportRankThirdCode
)} - ${proxy.$filters.localDictFilter('dictJobPro', p.reportSpecialtyThirdCode)}${
p.beforeId > 0 && p.sort ? `-分数:${p.sort}` : ''
}`
}
return _str
})
const isShowDetailComp = computed(() => useDataStore.personalDetail?.detail?.detail?.id)
const modelVisible = computed({
get: () => props.visible,
set: (v) => {
emits('update:visible', v)
}
})
watch(
() => modelVisible.value,
async (val) => {
if (val) {
await useDataStore.getPersonalDetail.getUserDetailInfo()
_index.value = cloneDeep(props.slideingData.curIndex)
// 根据分组规则限制请求
useReportF.loadData()
}
}
)
/**
* @description: 切换人员加载逻辑
* @param {*} type
* @return {*}
* @author: Lqi
*/
const handleLoadReportInfo = async (type) => {
try {
// init
useDataStore.resDataStoreData([
{ key: useDataStore.hoSpecial, data: useDataStore.initHoSpecial() }
])
const dom = document.querySelector('#reportForm')
const dom2 = document.querySelector('#annexWrap')
if (dom) {
dom.scrollTop = 0
} else if (dom2) {
dom2.scrollTop = 0
}
if (!type) {
_index.value--
} else {
_index.value++
}
const { id, report_id, user_id } = useDataStore.allIds.ids[_index.value]
await useDataStore.setPersonnelId(id)
await useDataStore.setReportId(report_id)
await useDataStore.setUserId(user_id)
await useDataStore.getPersonalDetail.getUserDetailInfo()
const isShowDetailed = useDataStore.personalDetail.detail?.detail?.isShowDetailed
const isMain = useDataStore.personalDetail.detail?.detail?.isMain
const hasShowDetailPower = useDataStore.personalDetail.detail?.detail?.hasShowDetailPower
const isAuxiliary = useDataStore.personalDetail.detail?.detail?.isAuxiliary
const planType = useDataStore.personalDetail.detail?.detail?.type
if (planType === 1) {
useDataStore.getPersonalDetail.getUserMainScoreInfo()
useDataStore.getPersonalDetail.getUserScoreInfo()
} else if (planType === 2) {
useDataStore.getPersonalDetail.getUserMainScoreInfo()
}
EventBus.nextNum++
// 修复跳转人员时左边tab因条件不成立无法选择的问题
if (
(isShowDetailed === 1 && (isMain || isAuxiliary || hasShowDetailPower)) ||
isShowDetailed === 2
) {
return
} else {
EventBus.leftCurrentKey = '1'
}
console.log('EventBus.leftCurrentKey', EventBus.leftCurrentKey)
} catch (error) {
throw new Error(error)
}
}
/**
* @description: 重新加载切换功能,保证allIds的可用
* @return {*}
* @author: Lqi
*/
const handleReloadAllIds = () => {
useDataStore.getAllIds()
}
const handleClose = () => {
EventBus.leftCurrentKey = '1'
emits('close')
}
</script>
<style lang="scss">
.full-modal {
.ant-modal {
max-width: 100%;
top: 0;
padding-bottom: 0;
margin: 0;
}
.ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh);
}
.ant-modal-body {
flex: 1;
}
}
.personal-switch {
position: absolute;
top: 20px;
right: 60px;
}
</style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-08-20 12:07:41
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-08-22 11:39:25
* @FilePath: /review-front/src/components/Details/score/index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-08-20 12:07:41
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-08-20 17:13:39
* @FilePath: /review-front/src/components/Details/score/index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<TenPoints></TenPoints>
</template>
<script setup>
import TenPoints from './tenPoints.vue'
</script>
<style lang="scss" scoped></style>
<template>
<div class="score-wrap">
<a-spin :spinning="submitLoading">
<div class="header-score">
<div class="header-score-item">
<span class="total"
>总分:{{
`${
useDataStore.personalDetail.score.sumScore
? useDataStore.personalDetail.score.sumScore + '分'
: '暂无'
}`
}}
</span>
<span
v-if="(type === 1 || type === 2) && !useDataStore.personalDetail.detail?.detail?.isMain"
>主审评分:{{ useDataStore.personalDetail.mainScore.sumScore }} </span
>
</div>
</div>
<div class="score-content">
<div class="content">
<h4 class="title">十分制</h4>
<a-form
ref="formRef"
:model="formState"
name="basic"
:label-col="{ span: 2 }"
:wrapper-col="{ span: 20 }"
autocomplete="off"
:rules="rules"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<div>
<p style="font-size: 20px; margin-bottom: 0.5rem">1.是否具备条件?(10分)</p>
<p style="font-size: 16px; margin-bottom: 0.5rem">
打分标准:0 ~ 6.6不同意推荐,6.7 ~ 6.9基本同意推荐,7 ~ 10同意推荐。
</p>
</div>
<a-form-item label="分数" name="sumScore">
<a-space>
<a-popover placement="bottom" trigger="focus">
<template #content>
<div class="button-grid">
<button
v-for="value in scoreValues"
:key="value"
class="grid-button"
@click="handleClick(value)"
>
{{ value }}
</button>
</div>
</template>
<a-input-number
v-model:value="formState.sumScore"
:min="0"
:max="10"
:step="0.1"
:precision="2"
style="width: 200px"
>
</a-input-number>
</a-popover>
<a-button
type="priamry"
:icon="h(DeleteOutlined)"
@click="formState.sumScore = ''"
danger
></a-button>
</a-space>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 2, span: 20 }">
<a-button :loading="submitLoading" type="primary" html-type="submit">保存</a-button>
</a-form-item>
</a-form>
</div>
</div>
</a-spin>
</div>
</template>
<script setup>
import { ref, h, computed, onMounted, getCurrentInstance, watch } from 'vue'
import { DeleteOutlined } from '@ant-design/icons-vue'
import { usePlanStoreHook } from '@/stores/modules/plan'
import { setScore } from '@/api/user/details'
import { useDataStoreHook } from '@/stores/modules/data'
// import { useAppStoreHook } from '@/stores/modules/app'
import { cloneDeep } from 'lodash-es'
import { useUserStoreHook } from '@/stores/modules/user'
const { proxy } = getCurrentInstance()
const usePlanStore = usePlanStoreHook()
const useUserStore = useUserStoreHook()
// const useAppStore = useAppStoreHook()
const visible = ref(false)
const formRef = ref(null)
const useDataStore = useDataStoreHook()
const formState = ref({
sumScore: '',
scoreContent: '',
id: '',
personnelId: useDataStore.personnelId,
planId: usePlanStore.planInfo.id,
groupId: usePlanStore.groupInfo.id,
oneVoteVetoList: [],
totalscore: '',
majorMatching: 0
})
const submitLoading = ref(false)
const type = computed(() => usePlanStore.groupInfo.type)
const scoreValues = ref([])
for (let i = 0; i <= 10; i += 0.3) {
if (i === 0) {
scoreValues.value.push(0)
} else {
scoreValues.value.push(i.toFixed(1)) // 使用 toFixed 保留一位小数
}
}
scoreValues.value.push(10)
const personalScore = computed(() => useDataStore.personalDetail.score)
const personnelId = computed(() => useDataStore.personnelId)
watch(
() => personalScore,
(val) => {
formState.value.sumScore = val.value.sumScore
},
{ deep: true }
)
watch(
() => personnelId.value,
(val) => {
formState.value.personnelId = val
}
)
onMounted(() => {
useDataStore.getPersonalDetail.getUserScoreInfo()
})
const rules = {
sumScore: [{ required: true, message: '分数不能为空', trigger: 'change' }]
}
const onFinish = async () => {
try {
if (formState.value.sumScore === 0) {
return proxy.$message.warning('分数不能为0')
}
submitLoading.value = true
const _items = [{ item: 'Q1', score: formState.value.sumScore }]
formState.value.scoreContent = _items
formState.value.totalscore = formState.value.sumScore
const res = await setScore(formState.value)
if (res.code === 200) {
proxy.$message.success(res.message)
useDataStore.getPersonalDetail.getUserScoreInfo()
if (useDataStore.personalDetail.detail.detail.mainId === useUserStore.expertUser.id) {
useDataStore.personalDetail.mainScore.sumScore = formState.value.sumScore
}
// getUserMainScoreInfo()
const _data = cloneDeep(useDataStore.personalDetail.detail.detail)
_data.expertScore = parseFloat(formState.value.sumScore)
if (usePlanStore.groupInfo.type === 2) {
_data.score = parseFloat(formState.value.sumScore)
}
useDataStore.updateReviewList(_data)
}
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
const onFinishFailed = () => {
console.log('onFinishFailed')
}
const handleClick = (val) => {
formState.value.sumScore = val
visible.value = false
}
// const getScoreItem = async () => {
// submitLoading.value = true
// const res = await getTemplateTreeScoreResult(
// useAppStore.reviewType,
// useDataStore.personnelId,
// usePlanStore.groupInfo.id
// )
// formState.value = Object.assign({}, formState.value, res.data)
// submitLoading.value = false
// }
</script>
<style lang="scss" scoped>
.button-grid {
display: flex;
flex-wrap: wrap; /* 允许换行 */
gap: 8px; /* 控制按钮间距 */
width: 450px;
}
.grid-button {
flex: 0 1 15%; /* 每个按钮占据15%的宽度 */
padding: 10px;
font-size: 16px;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
text-align: center;
cursor: pointer;
transition: background-color 0.2s;
}
.grid-button:hover {
background-color: #d0d0d0;
}
@media (max-width: 600px) {
.grid-button {
flex: 0 1 23%; /* 小屏幕下每个按钮占据23%的宽度 */
}
}
.score-wrap {
position: relative;
.header-score {
padding: 1rem;
top: 0;
left: 0;
right: 0;
width: 100%;
background-color: rgba(#4096ff, 0.8);
.header-score-item {
display: flex;
justify-content: space-around;
align-items: center;
color: #fff;
.total {
font-size: 1.25rem;
font-weight: 600;
}
}
}
.score-content {
height: calc(100vh - 14rem);
overflow-y: auto;
.content {
position: relative;
.title {
padding: 1rem 0;
margin-bottom: 2rem;
text-align: center;
font-weight: 600;
font-size: 18px;
border-bottom: 1px solid #d9d9d9;
}
}
}
}
</style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-10-11 10:07:45
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-12 15:43:53
* @FilePath: /review-front/src/components/Details/vote/index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="flex flex-c">
<a-space>
<a-button
class="success-btn"
:disabled="useDataStore.personalDetail?.detail?.detail.expertResult === 1"
:loading="loading"
@click="handleVote(1)"
>{{ useAppStore.voteBtnText[1] }}</a-button
>
<a-button
class="danger-btn"
:disabled="useDataStore.personalDetail?.detail?.detail.expertResult === 2"
:loading="loading"
@click="handleVote(2)"
>{{ useAppStore.voteBtnText[2] }}</a-button
>
</a-space>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
import { useAppStoreHook } from '@/stores/modules/app'
import { usePlanStoreHook } from '@/stores/modules/plan'
import { useDataStoreHook } from '@/stores/modules/data'
import { setVote } from '@/api/user/reviewList'
const useAppStore = useAppStoreHook()
const usePlanStore = usePlanStoreHook()
const useDataStore = useDataStoreHook()
const { proxy } = getCurrentInstance()
const loading = ref(false)
const handleVote = async (status) => {
try {
loading.value = true
const submitData = {
planId: usePlanStore.planInfo.id,
groupId: usePlanStore.groupInfo.id,
personnelIds: useDataStore.personalDetail?.detail?.detail.id,
status: status
}
const res = await setVote(submitData)
console.log('handleVote', res)
if (res.code === 200) {
setTimeout(() => {
useDataStore.getPersonalDetail.getUserDetailInfo()
loading.value = false
}, 300)
proxy.$message.success(res.message)
}
console.log('submitData', submitData)
// setVote
// console.log('props.record', props.record)
console.log('reviewListData', useDataStore.reviewListData)
} catch (error) {
loading.value = false
throw new Error(error)
}
}
</script>
<style lang="scss" scoped></style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-07 10:11:17
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-08-26 17:32:08
* @FilePath: /review-front/src/components/FormComp/EngageProSelect.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<a-cascader
v-model:value="value"
:options="options"
:fieldNames="{ label: 'name', value: 'code' }"
:show-search="{ filter }"
placeholder="请选择专业信息"
@change="handleChange"
/>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useDictsStoreHook } from '@/stores/modules/dicts'
const props = defineProps({
modelValue: Array
})
const { dicts } = useDictsStoreHook()
const emits = defineEmits(['update:modelValue', 'change'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emits('update:modelValue', value)
}
})
const options = ref(dicts['dictJobPro'] || [])
const filter = (inputValue, path) => {
return path.some((op) => op.name.includes(inputValue))
}
const handleChange = (vals) => {
emits('change', vals)
}
</script>
<style lang="scss" scoped></style>
<template>
<div class="slider-container">
<div class="slider-background"></div>
<div
class="slider"
:class="{ active: isDragging, success: isVerified }"
:style="{ left: dragOffset + 'px' }"
@mousedown="startDrag"
@touchstart="startDrag"
@mousemove="drag"
@touchmove="drag"
@mouseup="endDrag"
@touchend="endDrag"
>
<div class="slider-handle" :style="{ left: handleOffset + 'px' }"></div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const isDragging = ref(false)
const isVerified = ref(false)
const dragOffset = ref(0)
const handleOffset = ref(0)
let startX = 0
const startDrag = (event) => {
event.preventDefault()
isDragging.value = true
startX = event.type === 'mousedown' ? event.clientX : event.touches[0].clientX
}
const drag = (event) => {
event.preventDefault()
if (isDragging.value) {
const x = event.type === 'mousemove' ? event.clientX : event.touches[0].clientX
const offsetX = x - startX
dragOffset.value = offsetX < 0 ? 0 : offsetX
}
}
const endDrag = () => {
if (isDragging.value) {
isDragging.value = false
if (dragOffset.value >= 200) {
isVerified.value = true
handleOffset.value = 200
// 滑块验证成功后的回调
// 这里可以触发登录操作或者其他你需要的操作
console.log('验证成功!')
} else {
isVerified.value = false
dragOffset.value = 0
}
}
}
</script>
<style>
.slider-container {
position: relative;
width: 200px;
height: 50px;
overflow: hidden;
cursor: pointer;
}
.slider-background {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 50px;
background-image: url('background.jpg'); /* 背景图片路径 */
background-size: 400px 50px; /* 背景图片尺寸,与滑块的总长度一致 */
transition: background-position 0.3s;
}
.slider {
position: absolute;
top: 0;
left: 0;
width: 200px; /* 滑块总长度 */
height: 50px;
background-color: #fff;
border: 1px solid #ccc;
transition: left 0.3s;
}
.slider.active .slider-background {
background-position: -200px 0; /* 滑块拖动后的背景图位置 */
}
.slider-handle {
position: absolute;
top: 0;
left: 0;
width: 50px; /* 滑块宽度 */
height: 50px;
background-color: #ccc;
border-radius: 25px; /* 圆形滑块 */
transition: left 0.3s;
}
.slider.active .slider-handle {
background-color: green; /* 拖动时滑块颜色变化 */
}
</style>
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-07 10:11:36
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-04-10 15:59:28
* @FilePath: /review-front/src/components/FormComp/index.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import EngageProSelect from './EngageProSelect.vue'
import Verification from './LoginVerification.vue'
export { EngageProSelect, Verification }
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 10:45:04
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-02-22 14:40:41
* @FilePath: /review-front/src/components/HelloWorld.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup>
defineProps({
msg: {
type: String,
required: true
}
})
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
You’ve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
</h3>
</div>
</template>
<style lang="scss" scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings {
h1,
h3 {
text-align: center;
}
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-07 10:08:55
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-20 14:23:29
* @FilePath: /review-front/src/components/SearchBar/SearchBar.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="search-bar">
<a-space :size="32">
<SearchType></SearchType>
<a-space>
<a-input
v-model:value="value"
placeholder="检索: 姓名、编号"
style="width: 200px"
></a-input>
<a-button v-bind:loading="useDataStore.fetchLoading" type="primary" @click="handleSearch"
>检索</a-button
>
<a-button v-bind:loading="useDataStore.fetchLoading" @click="handleReset">重置</a-button>
<!-- <a-button type="link" @click="goHome">返回首页</a-button> -->
<a-button :loading="useDataStore.fetchLoading" type="link" @click="visible = !visible"
>展开
<CaretDownOutlined v-if="!visible" />
<CaretUpOutlined v-if="visible"
/></a-button>
</a-space>
</a-space>
<div v-show="visible" class="search-bar-expand">
<a-form>
<a-space :size="32">
<a-form-item label="申报专业">
<EngageProSelect
v-model="formState.engagePro"
style="width: 300px"
@change="handleJobProChnage"
></EngageProSelect>
</a-form-item>
<a-form-item label="是否满足">
<a-select
ref="select1"
placeholder="请选择"
v-model:value="formState.isQualified"
style="width: 120px"
@change="handleSelectCondition(0)"
>
<a-select-option
v-for="(item, index) in useDetail.opinionSelectOptions.qualifiedOptions"
:key="index"
:value="item.code"
><span>{{ item.name }}</span></a-select-option
>
</a-select>
</a-form-item>
<a-form-item label="是否同意">
<a-select
ref="select2"
placeholder="请选择"
v-model:value="formState.isAgreen"
style="width: 120px"
clearIcon
@change="handleSelectCondition(1)"
>
<a-select-option
v-for="(item, index) in useDetail.opinionSelectOptions.agreenOptions"
:key="index"
:value="item.code"
><span>{{ item.name }}</span></a-select-option
>
</a-select>
</a-form-item>
</a-space>
</a-form>
</div>
</div>
</template>
<script setup>
import { computed, ref, reactive } from 'vue'
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons-vue'
import SearchType from './SearchType.vue'
// import EngageProSelect from '../FormComp/EngageProSelect.vue'
import { useDataStoreHook } from '@/stores/modules/data'
// import { useRouter } from 'vue-router'
import { useDetailHook } from '@/hooks/useDetail'
import { useAppStoreHook } from '@/stores/modules/app'
const useDataStore = useDataStoreHook()
const useDetail = useDetailHook()
const useAppStore = useAppStoreHook()
// const router = useRouter()
const visible = ref(false)
const formState = reactive({
engagePro: [],
isAgreen: '',
isQualified: ''
})
// const searchLoading = ref(false)
const value = computed({
get() {
return useDataStore.listQuery.values?.searchKey
},
set(val) {
useDataStore.setQueryParams({ searchKey: val })
}
})
/**
* @description: 检索
* @return {*}
* @author: Lqi
*/
const handleSearch = () => {
// if (!useDataStore.searchKey) return
// searchLoading.value = true
useDataStore.getData()
useDataStore.getAllIds()
// setTimeout(() => {
// searchLoading.value = false
// }, 500)
}
const handleReset = () => {
useDataStore.setQueryParams({
searchKey: '',
reportSpecialtyFirstCode: '',
reportSpecialtySecondCode: '',
reportSpecialtyThirdCode: '',
isAgreen: '',
isQualified: ''
})
formState.engagePro = []
formState.isAgreen = ''
formState.isQualified = ''
useDataStore.getData()
}
const handleJobProChnage = (vals) => {
formState.engagePro = vals
if (vals) {
if (useAppStore.reviewType !== 'COMMON') {
useDataStore.setQueryParams({
reportSpecialtyFirstCode: formState.engagePro[0],
reportSpecialtySecondCode: formState.engagePro[1],
reportSpecialtyThirdCode: formState.engagePro[2]
})
} else {
useDataStore.setQueryParams({
valuesReportSpecialtyFirstCode: formState.engagePro[0],
valuesReportSpecialtySecondCode: formState.engagePro[1],
valuesReportSpecialtyThirdCode: formState.engagePro[2]
})
}
}
useDataStore.getData()
}
/**
* @description: 0 isQualified 1 isAgreen
* @param {*} condition
* @return {*}
* @author: Lqi
*/
const handleSelectCondition = (condition) => {
if (!condition) {
useDataStore.setQueryParams({
isQualified: formState.isQualified
})
} else {
useDataStore.setQueryParams({
isAgreen: formState.isAgreen
})
}
}
// const goHome = () => {
// router.push({ path: '/' })
// }
</script>
<style lang="scss" scoped>
.search-bar {
.title {
font-size: 1rem;
margin-bottom: 1rem;
}
.search-bar-expand {
margin-top: 1rem;
}
}
</style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-25 09:56:59
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-11-20 15:14:39
* @FilePath: /review-front/src/components/SearchBar/SearchType.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<a-radio-group v-model:value="value" :disabled="useDataStore.fetchLoading" button-style="solid">
<a-radio-button v-for="(item, index) in options" :key="index" :value="item.value">{{
item.label
}}</a-radio-button>
</a-radio-group>
</template>
<script setup>
import { computed } from 'vue'
import { useDataStoreHook } from '@/stores/modules/data'
import { usePlanStoreHook } from '@/stores/modules/plan'
const useDataStore = useDataStoreHook()
const usePlanStore = usePlanStoreHook()
const value = computed({
get() {
return useDataStore.listQuery.values?.type
},
async set(val) {
await useDataStore.setSearchType(val)
await useDataStore.setQueryParams({ type: val })
await useDataStore.getData()
await useDataStore.getAllIds()
}
})
const options = computed(() => {
let _list = useDataStore.searchBar.searchTypeOptions
if (usePlanStore.groupInfo.type > 0) {
_list = _list.filter((v) => v.value !== 5 && v.value !== 6)
}
return _list
})
</script>
<style lang="scss" scoped></style>
import SearchType from './SearchType.vue'
import SearchBar from './SearchBar.vue'
export { SearchType, SearchBar }
<template>
<a-drawer
v-model:open="staVisible"
class="custom-class"
root-class-name="root-class-name"
size="large"
title="评审统计"
placement="right"
>
<div class="charts-wrap">
<div v-show="!loading" class="charts-item">
<a-divider class="title">基础统计</a-divider>
<div class="desc">
<div class="desc-content">
<div class="desc-item">
<p>{{ staData.totalCount || 0 }}</p>
<p>参评人员</p>
</div>
<div class="desc-item">
<p v-if="usePlanStore.groupInfo.type > 0">{{ staData.scoreCount }}</p>
<p v-else>{{ Number(staData.passCount + staData.noPassCount) || 0 }}</p>
<p>已评人数</p>
</div>
<div class="desc-item">
<p>{{ staData.isMainCount || 0 }}</p>
<p>主审人数</p>
</div>
<div class="desc-item">
<p>{{ staData.auxiliaryCount || 0 }}</p>
<p>辅审人数</p>
</div>
</div>
</div>
</div>
<div v-show="!loading" class="charts-item">
<a-divider class="title">进度统计</a-divider>
<div class="progress-chart">
<a-progress
:stroke-color="{
'0%': '#108ee9',
'100%': '#87d068'
}"
:percent="parseFloat(progress)"
/>
</div>
<div class="desc">
<p>
目前为止您的评审进度完成
<span>{{ progress || 0 }}%</span>
<template v-if="usePlanStore.groupInfo.type >= 1">
,您整场打出的平均分为 <span>{{ staData.avgScore || 0 }} 分。</span>
</template>
</p>
</div>
</div>
<div v-show="!loading" class="charts-item">
<!-- <a-divider class="title">评审情况</a-divider> -->
<div id="pieChart" style="height: 30rem"></div>
<div class="desc">
<p>
<template v-if="usePlanStore.groupInfo.type === 0">
通过率:<span>
{{ ((staData.passCount / staData.totalCount) * 100).toFixed(2) || 0 }}% ({{
staData.passCount
}}人) &nbsp;</span
>
不通过率:<span>
{{ ((staData.noPassCount / staData.totalCount) * 100).toFixed(2) || 0 }}% ({{
staData.noPassCount
}}人) &nbsp;</span
>
已评审:
<span>
{{
(((staData.passCount + staData.noPassCount) / staData.totalCount) * 100).toFixed(
2
) || 0
}}
% ({{ staData.passCount + staData.noPassCount }}人)&nbsp;</span
>
</template>
</p>
</div>
</div>
<template v-if="loading">
<a-skeleton-button block />
<br />
<br />
<br />
<a-skeleton :paragraph="{ rows: 3 }" />
<br />
<br />
<a-skeleton-button block />
<br />
<br />
<br />
<a-skeleton :paragraph="{ rows: 2 }" />
<br />
<br />
<a-skeleton-image style="margin-left: 150px" active />
</template>
</div>
</a-drawer>
</template>
<script setup>
import { ref, onMounted, watch, computed, nextTick } from 'vue'
import { useAppConfigHook } from '@/hooks/useAppConfig'
import { getExpertStatistics } from '@/api/user/statistics'
import { usePlanStoreHook } from '@/stores/modules/plan'
import { Chart } from '@antv/g2'
const usePlanStore = usePlanStoreHook()
const { staVisible } = useAppConfigHook()
const staData = ref({})
const loading = ref(false)
onMounted(() => {
// init()
})
const progress = computed(() => {
const noPassCount = staData.value.noPassCount || 0
const passCount = staData.value.passCount || 0
const totalCount = staData.value.totalCount || 0
const scoreCount = staData.value.scoreCount || 0
return [
(((noPassCount + passCount) / totalCount) * 100).toFixed(2),
((scoreCount / totalCount) * 100).toFixed(2),
((scoreCount / totalCount) * 100).toFixed(2)
][usePlanStore.groupInfo.type]
})
watch(staVisible, () => {
if (staVisible.value) {
init()
}
})
const init = async () => {
try {
loading.value = true
const res = await getExpertStatistics(usePlanStore.planInfo.id, usePlanStore.groupInfo.id)
staData.value = res.data
loading.value = false
setTimeout(() => {
nextTick(() => {
initPieChart()
})
}, 500)
} catch (error) {
loading.value = false
throw new Error(error)
}
}
const initPieChart = () => {
let data = []
const voteData = [
{
item: '通过率',
count: staData.value.passCount,
percent: staData.value.passCount / staData.value.totalCount,
color: 'red'
},
{
item: '不通过率',
count: staData.value.noPassCount,
percent: staData.value.noPassCount / staData.value.totalCount,
color: 'black'
}
]
const scoreData = [
{
item: '已评审',
count: staData.value.scoreCount,
percent: staData.value.scoreCount / staData.value.totalCount
},
{
item: `待评审`,
count: staData.value.waitCount,
percent: staData.value.waitCount / staData.value.totalCount
}
]
if (usePlanStore.groupInfo.type > 0) {
data = scoreData
} else {
data = voteData
}
// var chart = echarts.init(document.getElementById('pieChart'));
// eslint-disable-next-line no-undef
const chart = new Chart({
container: 'pieChart',
autoFit: true
})
chart.coordinate({ type: 'theta', outerRadius: 0.8 })
chart
.interval()
.data(data)
.transform({ type: 'stackY' })
.encode('y', 'percent')
.encode('color', 'item', ['#ff7875', '#95de64'])
.legend('color', { position: 'bottom', layout: { justifyContent: 'center' } })
.label({
position: 'outside',
text: (data) => `${data.item}: ${(data.percent * 100).toFixed(2)}%`
})
.tooltip((data) => ({
name: data.item,
value: `${(data.percent * 100).toFixed(2)}%`
}))
chart.render()
}
</script>
<style lang="scss" scoped>
.charts-wrap {
.charts-item {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
// margin-bottom: 2rem;
.title {
font-size: 18px;
font-weight: 600;
}
.desc {
padding: 0.4rem 1rem;
.desc-content {
display: flex;
justify-content: space-around;
.desc-item {
p:first-child {
color: #1677ff;
font-weight: 600;
font-size: 28px;
}
p:last-child {
font-size: 14px;
color: #8c8c8c;
}
}
}
p {
font-size: 16px;
span {
color: #1677ff;
font-weight: 600;
}
}
}
.progress-chart {
padding: 0 4rem;
}
}
}
:deep(.ant-skeleton.ant-skeleton-element .ant-skeleton-image) {
width: 400px;
height: 400px;
margin: 0 auto;
}
</style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-12-12 16:52:48
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-12 17:14:35
* @FilePath: /review-front/src/components/Table/index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="cus-table">
<a-table></a-table>
</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-22 10:45:04
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-03-07 16:23:55
* @FilePath: /review-front/src/components/WelcomeItem.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped></style>
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})
import SelectJury from './selectJury/index.vue'
import SelectPlan from './selectPlan/index.vue'
export { SelectJury, SelectPlan }
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-12-12 15:10:20
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-16 16:13:00
* @FilePath: /review-front/src/components/admin/selectJury/index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<a-select
v-model:value="internalValue"
show-search
:disabled="disabled"
placeholder="选择评委会"
style="width: 200px"
:options="options"
:filter-option="filterOption"
:fieldNames="{
label: 'unitName',
value: 'unitId'
}"
allowClear
@change="handleChange"
></a-select>
</template>
<script setup>
import { ref, onMounted, defineEmits, defineProps, computed } from 'vue'
import { getJuryList } from '@/api/admin/review'
const props = defineProps({
disabled: {
type: Boolean,
default: false
},
value: {
type: [String, Number],
default: ''
}
})
const emits = defineEmits(['update:value', 'change'])
const options = ref([])
const internalValue = computed({
get() {
return props.value
},
set(val) {
emits('update:value', val)
}
})
const listQuery = ref({
current: 1,
size: 9999,
params: {
eq: {
alias_p: {
type: 0,
status: 1
}
},
like: {
or: {
unit_name: ''
}
}
}
})
onMounted(() => {
console.log('onMounted', props)
// internalValue.value = props.getJury()
getJury()
})
/**
* @description: 获取评委会
* @return {*}
* @author: Lqi
*/
const getJury = async () => {
try {
const res = await getJuryList(listQuery.value)
options.value = res.data.records || []
} catch (error) {
console.error('Error fetching jury list:', error)
}
}
const handleChange = (value, option) => {
console.log('handleChange')
console.log(option)
emits('change', option)
}
const filterOption = (input, option) => {
return option.unitName.indexOf(input) >= 0
}
</script>
<style lang="scss" scoped></style>
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-12-12 15:10:20
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-17 10:18:21
* @FilePath: /review-front/src/components/admin/selectJury/index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<a-select
v-model:value="internalValue"
show-search
:disabled="disabled"
placeholder="选择计划"
style="width: 200px"
:options="options"
:filter-option="filterOption"
:fieldNames="{
label: 'name',
value: 'id'
}"
allowClear
@change="handleChange"
></a-select>
</template>
<script setup>
import { ref, onMounted, defineEmits, defineProps, computed, defineExpose } from 'vue'
import { getPlanList } from '@/api/admin/plan'
const props = defineProps({
disabled: {
type: Boolean,
default: false
},
value: {
type: [String, Number],
default: ''
}
})
const emits = defineEmits(['update:value', 'change'])
const options = ref([])
const internalValue = computed({
get() {
return props.value
},
set(val) {
emits('update:value', val)
}
})
onMounted(() => {})
/**
* @description: 获取评委会
* @return {*}
* @author: Lqi
*/
const getPlan = async (unitId, type) => {
try {
const res = await getPlanList(unitId, type)
options.value = res.data.reverse() || []
} catch (error) {
console.error('Error fetching plan list:', error)
}
}
const handleChange = (value, option) => {
console.log('handleChange')
console.log(option)
emits('change', option)
}
const filterOption = (input, option) => {
return option.unitName.indexOf(input) >= 0
}
defineExpose({
getPlan
})
</script>
<style lang="scss" scoped></style>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-06 14:30:23
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-10 17:11:50
* @FilePath: /review-front/src/config/api.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// API枚举
export const APIENUM = {
COMMON: 'api',
EDU: 'api-edu',
HO: 'api-ho'
}
export const baseAPI = APIENUM[JSON.parse(localStorage.getItem('plan'))?.planInfo.reviewType]
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-23 11:14:20
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-07-22 10:02:18
* @FilePath: /review-front/src/config/index.js
* @Description:
*/
const SYSTEM_TITLE = '专业技术人才评审系统'
const COPYRIGHT = '云南墨斯墨人才服务有限公司'
export { SYSTEM_TITLE, COPYRIGHT }
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-23 11:45:59
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-17 16:12:13
* @FilePath: /review-front/src/config/url.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const ENV = process.env.NODE_ENV
// export const DEV_BASE_URL = 'http://116.52.144.172:8100'
// export const DEV_BASE_URL = 'http://182.245.127.25'
// http://192.168.1.17:8000
// export const DEV_BASE_URL = 'http://182.245.127.25'
// export const DEV_BASE_URL = 'http://182.245.127.25'
// export const DEV_BASE_URL = 'http://192.168.1.5'
export const DEV_BASE_URL = 'http://192.168.1.10:9000'
export const PROD_BASE_URL = 'http://182.245.127.25'
// export const PROD_BASE_URL = 'http://192.168.1.5'
// export const PROD_BASE_URL = 'http://116.52.144.172:7777'
export const development = 'development'
export const production = 'production'
// export const production = 'production'
const server = {
baseUrl: {
development: DEV_BASE_URL,
production: PROD_BASE_URL
}
}
/**
* @description api请求基础路径
*/
export const API_BASE_URL = server.baseUrl[ENV]
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-02-23 15:47:23
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-16 09:49:22
* @FilePath: /review-front/src/config/wihteList.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/** 免登录白名单(匹配路由 path) */
const whiteListByPath = ['/login', '/loginObserver', '/signature', '/adminLogin']
/** 免登录白名单(匹配路由 name) */
const whiteListByName = []
/** 判断是否在白名单 */
const isWhiteList = (to) => {
// path 和 name 任意一个匹配上即可
return whiteListByPath.indexOf(to.path) !== -1 || whiteListByName.indexOf(to.name) !== -1
}
export default isWhiteList
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-12 14:56:23
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-09-02 16:13:56
* @FilePath: /review-front/src/hooks/useConfig.js
* @Description: 配置App的Hooks
*/
// import { useAppStoreHook } from '@/stores/modules/app'
// import { message } from 'ant-design-vue'
import { ref } from 'vue'
// import { APIENUM } from '@/config/api'
// const useAppStore = useAppStoreHook()
// 统计弹窗控制
const staVisible = ref(false)
// const baseAPI = computed(() => {
// console.log('[useAppStore.reviewType', useAppStore.reviewType)
// return APIENUM[useAppStore.reviewType]
// })
// const configure = ({ messageType }) => {
// try {
// useAppStore.setAppLoding(true)
// // 配置表决按钮文字信息
// useAppStore.setVoteBtnText(messageType)
// } catch (error) {
// useAppStore.setAppLoding(false)
// console.error(error)
// message.error('配置信息错误,请联系管理员')
// } finally {
// useAppStore.setAppLoding(false)
// }
// }
const setStaVisible = (value) => {
staVisible.value = value
}
export function useAppConfigHook() {
return { staVisible, setStaVisible }
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-15 16:33:41
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-10 15:35:43
* @FilePath: /review-front/src/hooks/useDetail.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { reactive, ref, computed } from 'vue'
import { useAppStoreHook } from '@/stores/modules/app'
import { usePlanStoreHook } from '@/stores/modules/plan'
import { useDictsStoreHook } from '@/stores/modules/dicts'
const useAppStore = useAppStoreHook()
const usePlanStore = usePlanStoreHook()
const useDictsStore = useDictsStoreHook()
const leftTabs = reactive({
COMMON: [
{ key: '1', tab: '申报表', name: 'reportForm' },
{ key: '2', tab: '附件材料', name: 'annex' }
],
EDU: [
{ key: '1', tab: '申报表', name: 'reportForm' },
{ key: '2', tab: '附件材料', name: 'annex' }
]
})
const configState = ref({
// 复制意见
copyOpinion: true,
// 详情页弹框
detailVisible: false,
// 是否修改了意见为保存
isEditNotSave: false,
// 详情左边当前tabs的key
leftCurrentKey: '1',
// 详情右边当前tabs的key
rightCurrentKey: '1'
})
const opinionSelectOptions = reactive({
qualifiedOptions: useDictsStore.getDictByParentId('dictTree', 645),
agreenOptions: useDictsStore.getDictByParentId('dictTree', 649)
})
const setConfigState = ({ ...configs }) => {
configState.value = Object.assign(configState.value, configs)
}
/**
* @description: 详情页分屏左边tab显示
* @param {*} computed
* @return {*}
* @author: Lqi
*/
const leftTabsList = computed(() => leftTabs[useAppStore.reviewType])
const rightTabs = reactive([
{ key: '1', tab: '意见', name: 'opinion' },
{ key: '2', tab: '打分', name: 'scoring' }
// { key: '3', tab: '表决', name: 'vote' }
])
const rightTabsList = computed(() => {
const type = usePlanStore.groupInfo.type
let _list = rightTabs.filter((v) => {
if (!usePlanStore.groupInfo.isSetMessage) {
return v.name !== 'opinion'
}
if (type < 1) {
return v.name !== 'scoring'
}
if (type >= 1) {
return v.name !== 'vote'
}
return true
})
console.log('rightTabsList', type, rightTabsList)
return _list
})
export function useDetailHook() {
return { leftTabsList, rightTabsList, configState, setConfigState, opinionSelectOptions }
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-04-02 10:48:30
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-04-02 11:03:58
* @FilePath: /review-front/src/hooks/useMessage.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { ref } from 'vue'
// import { message } from 'ant-design-vue'
const a = ref('')
// const { proxy } = getCurrentInstance()
// // const [messageApi] = message.useMessage()
// const message = (message, type = 'error', duration = 3000) => {
// proxy.$message[type]({ message, duration })
// }
export function useMessageHook() {
return { a }
}
/*
* @Author: lqi 575920678@qq.com
* @Date: 2024-03-13 14:42:47
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-10-30 15:41:14
* @FilePath: /review-front/src/hooks/useTableConfig.js
* @Description: table list render hook
*/
import { reactive, computed } from 'vue'
import { useAppStoreHook } from '@/stores/modules/app'
const { reviewType } = useAppStoreHook()
const TABLE_COLUMNS = reactive({
COMMON: [
{ title: '编号', dataIndex: 'number', key: 'number', fixed: 'feft', width: 120 },
{ title: '结果', dataIndex: 'expertResult', key: 'expertResult', fixed: 'feft', width: 80 },
{ title: '分数', dataIndex: 'sort', key: 'sort', width: 80 },
{ title: '姓名', dataIndex: 'userName', key: 'userName', width: 120 },
{ title: '单位', dataIndex: 'unitName', key: 'unitName', width: 300 },
{ title: '现从事专业', dataIndex: 'engagePro', key: 'engagePro', width: 280 },
{ title: '申报专业', dataIndex: 'reportPro', key: 'reportPro', width: 280 },
{ title: '申报职称', dataIndex: 'reportRank', key: 'reportRank', width: 280 },
{ title: '申报等级', dataIndex: 'reportLevel', key: 'reportLevel', width: 120 },
{ title: '操作', dataIndex: 'operation', key: 'operation', fixed: 'right', width: 500 }
],
EDU: [
{ title: '编号', dataIndex: 'number', key: 'number', width: 120 },
{ title: '结果', dataIndex: 'expertResult', key: 'expertResult', fixed: 'feft', width: 80 },
{ title: '学科组得分', dataIndex: 'sort', key: 'sort', fixed: 'feft', width: 110 },
{ title: '姓名', dataIndex: 'userName', key: 'userName', width: 100 },
{ title: '单位', dataIndex: 'unitName', key: 'unitName', width: 240 },
{ title: '性别', dataIndex: 'sex', key: 'sex', width: 40 },
{ title: '出生年月', dataIndex: 'birthday', key: 'birthday', width: 120 },
{ title: '学历', dataIndex: 'educationCode', key: 'educationCode', width: 120 },
{ title: '参加工作时间', dataIndex: 'jobWorkTime', key: 'jobWorkTime', width: 120 },
{ title: '聘任时间', dataIndex: 'hireTime', key: 'hireTime', width: 120 },
{ title: '教师资格证', dataIndex: 'qualificationCode', key: 'qualificationCode', width: 80 },
{ title: '申报职称', dataIndex: 'reportRank', key: 'reportRank', width: 100 },
{
title: '申报专业',
dataIndex: 'reportPro',
key: 'reportPro',
width: 100
},
{ title: '履职考核等次', dataIndex: 'gradeCode', key: 'gradeCode', width: 200 },
{ title: '乡村学校或薄弱学校工作年限', dataIndex: 'years', key: 'years', width: 140 },
{
title: '主(辅)审意见',
dataIndex: 'order',
key: 'order',
fixed: 'right',
width: 200,
children: [
{ title: '是否具备', dataIndex: 'isQualified', key: 'isQualified', width: 100 },
{ title: '是否同意', dataIndex: 'isAgreen', key: 'isAgreen', width: 100 }
]
},
{ title: '操作', dataIndex: '', key: 'operation', fixed: 'right', width: 500 }
],
HO: [
{ title: '编号', dataIndex: 'number', key: 'number', fixed: 'feft', width: 100 },
{ title: '结果', dataIndex: 'expertResult', key: 'expertResult', fixed: 'feft', width: 80 },
{ title: '姓名', dataIndex: 'userName', key: 'userName', width: 100 },
{ title: '考试成绩', dataIndex: 'examScore', key: 'examScore', width: 140 },
{ title: '单位', dataIndex: 'unitName', key: 'unitName', width: 200 },
// { title: '单位性质', dataIndex: 'unitType', key: 'unitType', width: 40 },
{ title: '申报职称', dataIndex: 'reportRank', key: 'reportRank', width: 140 },
{
title: '申报专业',
dataIndex: 'reportPro',
key: 'reportPro',
width: 140
},
{
title: '是否新冠一线',
dataIndex: 'reportScoreExtend',
key: 'reportScoreExtend',
width: 50,
children: [
{ title: '是否一线', dataIndex: 'isCovid19Front', key: 'isCovid19Front', width: 100 },
{ title: '类型', dataIndex: 'personnelType', key: 'personnelType', width: 120 },
{ title: '起止时间', dataIndex: 'covid19Time', key: 'covid19Time', width: 200 }
]
},
{
title: '主(辅)审意见',
dataIndex: 'order',
key: 'order',
fixed: 'right',
width: 200,
children: [
{ title: '是否具备', dataIndex: 'isQualified', key: 'isQualified', width: 100 },
{ title: '是否同意', dataIndex: 'isAgreen', key: 'isAgreen', width: 100 }
]
},
// { title: '是否享受政策倾斜', dataIndex: 'isEnjoyPolicy', key: 'isEnjoyPolicy', width: 40 },
{ title: '操作', dataIndex: '', key: 'operation', fixed: 'right', width: 450 }
]
})
const columns = computed(() => {
let _cols = TABLE_COLUMNS[reviewType]
const type = JSON.parse(localStorage.getItem('plan')).groupInfo.type
// 教育过滤col
if (reviewType === 'EDU' && type) {
_cols = _cols.filter((v) => v.key !== 'sort')
}
return _cols
})
const hoCovid19Format = [
{
type: '省内一线/援鄂',
desc: '可提前1年申报高一级职称,符合申报条件的免于参加实践能力考试,参加疫情防控经历可视同为一年基层工作经历,视同为完成当年继续教育学分学时;参加援鄂的专业技术人员援助工作经历可视同为到上级机构进修工作经历;对于在抗疫一线工作中做出突出贡献的,受到省部级以上表彰奖励的,可以特殊人才晋升高级职称方式直接申报高一级职称评审(须完成本专业规定的工作量)'
},
{
type: '参加疫情防控但未认定为一线人员',
desc: '医务人员按照政府统一部署、卫生健康部门调派或医疗卫生机构要求,直接参与疫情防控一线工作但未被认定为一线医务人员的(须经单位核定,并提供相关证明材料),在职称评聘时其参加疫情防控经历可视同为半年基层工作经历(多次调派参加疫情防控工作但未被认定为一线的医务人员也只能视同为半年基层工作经历)'
},
{
type: '过渡期一线人员',
desc: '按照《云南省疫情防控新阶段保护关心爱护医务人员十条措施》(云应疫指发〔2023〕1号),对疫情防控救治工作中表现突出的医务人员,考核确定为优秀的,可享受1次提前1年申报(报考)上一级卫生专业技术职称(资格)。'
}
]
export function useTable() {
return { columns, hoCovid19Format }
}
<!--
* @Author: lqi 575920678@qq.com
* @Date: 2024-12-11 15:51:02
* @LastEditors: lqi 575920678@qq.com
* @LastEditTime: 2024-12-16 09:30:30
* @FilePath: /review-front/src/layouts/adminLayout.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<a-spin :spinning="useAppStore.appLoading" tip="Loading..." size="large">
<a-layout ref="app" class="app-wrapper amdin-wrapper">
<a-layout-sider v-model:collapsed="state.collapsed" :trigger="null" collapsible>
<div class="logo" />
<a-menu
v-model:selected-keys="state.currentRoute"
:open-keys="openKeys"
theme="dark"
mode="inline"
>
<template v-for="item in menus">
<a-sub-menu v-if="item.children" :key="item.key">
<template #icon>
<component :is="item.icon" />
</template>
<template #title>
<span>{{ item.label }}</span>
</template>
<template #default>
<a-menu-item v-for="child in item.children" :key="child.key">
<template #icon>
<component :is="child.icon" />
</template>
<router-link :to="child.key">{{ child.label }}</router-link>
</a-menu-item>
</template>
</a-sub-menu>
<a-menu-item v-else :key="item.key">
<template #icon>
<component :is="item.icon" />
</template>
<router-link :to="item.key">{{ item.label }}</router-link>
</a-menu-item>
</template>
</a-menu>
</a-layout-sider>
<a-layout>
<a-layout-header class="admin-header">
<div class="collapsed">
<menu-unfold-outlined
v-if="state.collapsed"
class="trigger"
@click="() => (state.collapsed = !state.collapsed)"
/>
<menu-fold-outlined
v-else
class="trigger"
@click="() => (state.collapsed = !state.collapsed)"
/>
</div>
<div class="user">
<a-dropdown>
<a class="ant-dropdown-link" @click.prevent>
管理员
<DownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item>
<a href="javascript:;" @click="handleLogout">退出登录</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</a-layout-header>
<a-layout-content
:style="{
margin: '24px 16px 0 16px',
padding: '24px',
background: '#fff',
minHeight: '280px'
}"
>
<router-view></router-view>
</a-layout-content>
<Footer></Footer>
</a-layout>
</a-layout>
</a-spin>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
// import Header from './components/SysHeader.vue'
// import Main from './components/SysMain.vue'
import Footer from './components/SysFooter.vue'
import { useAppStoreHook } from '@/stores/modules/app'
import { useUserStoreHook } from '@/stores/modules/user'
import { MenuUnfoldOutlined, MenuFoldOutlined, DownOutlined } from '@ant-design/icons-vue'
import { routes } from '@/router'
const useAppStore = useAppStoreHook()
const useUserStore = useUserStoreHook()
const route = useRoute()
const app = ref(null)
const state = reactive({
collapsed: false,
currentRoute: [route.fullPath]
})
onMounted(() => {})
const menus = computed(() => {
const _rList = routes.filter((v) => v.meta?.isAdminMenu)
console.log(_rList[0].children)
return analysisMenu(_rList[0].children)
})
const openKeys = computed(() => {
return getParentKeys(...state.currentRoute, menus.value)
})
const analysisMenu = (data) => {
const transformNode = (node) => {
console.log(node.meta)
try {
const transformedNode = {
key: node.path,
icon: node.meta?.icon,
label: node.meta?.title || '',
title: node.meta?.title || ''
}
if (node.children && node.children.length > 0) {
transformedNode.children = node.children.map(transformNode)
}
return transformedNode
} catch (error) {
throw new Error(error)
}
}
return data.map(transformNode)
}
const getParentKeys = (path, menuData, parentKeys = []) => {
console.log(path)
for (const item of menuData) {
if (item.key === path) {
return parentKeys
}
if (item.children) {
const keys = getParentKeys(path, item.children, [...parentKeys, item.key])
if (keys.length) return keys
}
}
return []
}
const handleLogout = async () => {
await useUserStore.logoutByAdmin()
}
</script>
<style lang="scss" scoped>
.amdin-wrapper {
height: 100%;
.admin-header {
display: flex;
justify-content: space-between;
padding: 0 24px;
background-color: #fff;
.trigger {
font-size: 20px;
line-height: 64px;
padding: 0;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #1890ff;
}
}
}
.logo {
height: 32px;
background: rgba(255, 255, 255, 0.3);
margin: 16px;
}
}
</style>
This diff is collapsed. Click to expand it.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment