refactor: 移除未使用的图片资源并更新环境配置
feat: 恢复用户菜单和API权限获取功能 fix: 修正登录页面样式和功能 refactor: 清理API模块中未使用的接口 style: 更新加载动画显示标题 chore: 移除未使用的依赖项
2
web/.env
@ -1,3 +1,3 @@
|
|||||||
VITE_TITLE = '成都文化产权交易所'
|
VITE_TITLE = 'Vue FastAPI Admin'
|
||||||
|
|
||||||
VITE_PORT = 3100
|
VITE_PORT = 3100
|
||||||
@ -5,4 +5,4 @@ VITE_PUBLIC_PATH = '/'
|
|||||||
VITE_USE_PROXY = true
|
VITE_USE_PROXY = true
|
||||||
|
|
||||||
# base api
|
# base api
|
||||||
VITE_BASE_API = 'https://value.cdcee.net/api/v1'
|
VITE_BASE_API = '/api/v1'
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
VITE_PUBLIC_PATH = '/'
|
VITE_PUBLIC_PATH = '/'
|
||||||
|
|
||||||
# base api
|
# base api
|
||||||
VITE_BASE_API = 'https://value.cdcee.net/api/v1'
|
VITE_BASE_API = '/api/v1'
|
||||||
|
|
||||||
# 是否启用压缩
|
# 是否启用压缩
|
||||||
VITE_USE_COMPRESS = true
|
VITE_USE_COMPRESS = true
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export const PROXY_CONFIG = {
|
|||||||
* @转发路径 http://localhost:9999/api/v1/user
|
* @转发路径 http://localhost:9999/api/v1/user
|
||||||
*/
|
*/
|
||||||
'/api/v1': {
|
'/api/v1': {
|
||||||
target: 'http://124.222.245.240:8080',
|
target: 'http://127.0.0.1:9999',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
import Unocss from 'unocss/vite'
|
import Unocss from 'unocss/vite'
|
||||||
|
|
||||||
// rollup打包分析插件
|
// rollup打包分析插件
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import visualizer from 'rollup-plugin-visualizer'
|
||||||
// 压缩
|
// 压缩
|
||||||
import viteCompression from 'vite-plugin-compression'
|
import viteCompression from 'vite-plugin-compression'
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
<div class="right-0 bottom-0 loading-spin-item loading-delay-1500"></div>
|
<div class="right-0 bottom-0 loading-spin-item loading-delay-1500"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="loading-title"><%= title %></div> -->
|
<div class="loading-title"><%= title %></div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/resource/loading.js"></script>
|
<script src="/resource/loading.js"></script>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
9149
web/package-lock.json
generated
@ -2,7 +2,6 @@
|
|||||||
"name": "vue-fastapi-admin-web",
|
"name": "vue-fastapi-admin-web",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
@ -16,13 +15,11 @@
|
|||||||
"@iconify/json": "^2.2.228",
|
"@iconify/json": "^2.2.228",
|
||||||
"@iconify/vue": "^4.1.1",
|
"@iconify/vue": "^4.1.1",
|
||||||
"@unocss/eslint-config": "^0.55.0",
|
"@unocss/eslint-config": "^0.55.0",
|
||||||
"@vicons/ionicons5": "^0.13.0",
|
|
||||||
"@vueuse/core": "^10.3.0",
|
"@vueuse/core": "^10.3.0",
|
||||||
"@zclzone/eslint-config": "^0.0.4",
|
"@zclzone/eslint-config": "^0.0.4",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"dayjs": "^1.11.9",
|
"dayjs": "^1.11.9",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"echarts": "^5.4.3",
|
|
||||||
"eslint": "^8.46.0",
|
"eslint": "^8.46.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"naive-ui": "^2.34.4",
|
"naive-ui": "^2.34.4",
|
||||||
|
|||||||
8
web/pnpm-lock.yaml
generated
@ -17,9 +17,6 @@ importers:
|
|||||||
'@unocss/eslint-config':
|
'@unocss/eslint-config':
|
||||||
specifier: ^0.55.0
|
specifier: ^0.55.0
|
||||||
version: 0.55.7(eslint@8.57.0)(typescript@5.5.4)
|
version: 0.55.7(eslint@8.57.0)(typescript@5.5.4)
|
||||||
'@vicons/ionicons5':
|
|
||||||
specifier: ^0.13.0
|
|
||||||
version: 0.13.0
|
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.3.0
|
specifier: ^10.3.0
|
||||||
version: 10.11.0(vue@3.4.34(typescript@5.5.4))
|
version: 10.11.0(vue@3.4.34(typescript@5.5.4))
|
||||||
@ -536,9 +533,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
||||||
|
|
||||||
'@vicons/ionicons5@0.13.0':
|
|
||||||
resolution: {integrity: sha512-zvZKBPjEXKN7AXNo2Na2uy+nvuv6SP4KAMQxpKL2vfHMj0fSvuw7JZcOPCjQC3e7ayssKnaoFVAhbYcW6v41qQ==}
|
|
||||||
|
|
||||||
'@vitejs/plugin-vue@4.6.2':
|
'@vitejs/plugin-vue@4.6.2':
|
||||||
resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
|
resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
@ -2993,8 +2987,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- rollup
|
- rollup
|
||||||
|
|
||||||
'@vicons/ionicons5@0.13.0': {}
|
|
||||||
|
|
||||||
'@vitejs/plugin-vue@4.6.2(vite@4.5.3(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))':
|
'@vitejs/plugin-vue@4.6.2(vite@4.5.3(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))':
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 4.5.3(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3)
|
vite: 4.5.3(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3)
|
||||||
|
|||||||
@ -5,14 +5,6 @@ export default {
|
|||||||
getUserInfo: () => request.get('/base/userinfo'),
|
getUserInfo: () => request.get('/base/userinfo'),
|
||||||
getUserMenu: () => request.get('/base/usermenu'),
|
getUserMenu: () => request.get('/base/usermenu'),
|
||||||
getUserApi: () => request.get('/base/userapi'),
|
getUserApi: () => request.get('/base/userapi'),
|
||||||
// 手机号
|
|
||||||
registerPhone: (data) => request.post('/app-user/register', data, { noNeedToken: true }),
|
|
||||||
loginPhone: (data) => request.post('/app-user/login', data, { noNeedToken: true }),
|
|
||||||
// pages
|
|
||||||
getIndustryList: () => request.get('/industry/list'),
|
|
||||||
getHistoryList: (params) => request.get('/app-valuations/', { params }),
|
|
||||||
valuations: (data = {}) => request.post('/app-valuations/', data),
|
|
||||||
deleteValuations: (params = {}) => request.delete(`/app-valuations/${params.id}`),
|
|
||||||
// profile
|
// profile
|
||||||
updatePassword: (data = {}) => request.post('/base/update_password', data),
|
updatePassword: (data = {}) => request.post('/base/update_password', data),
|
||||||
// users
|
// users
|
||||||
@ -47,28 +39,4 @@ export default {
|
|||||||
deleteDept: (params = {}) => request.delete('/dept/delete', { params }),
|
deleteDept: (params = {}) => request.delete('/dept/delete', { params }),
|
||||||
// auditlog
|
// auditlog
|
||||||
getAuditLogList: (params = {}) => request.get('/auditlog/list', { params }),
|
getAuditLogList: (params = {}) => request.get('/auditlog/list', { params }),
|
||||||
// esg
|
|
||||||
getESGList: (params = {}) => request.get('/esg/list', { params }),
|
|
||||||
getESGById: (params = {}) => request.get('/esg/get', { params }),
|
|
||||||
createESG: (data = {}) => request.post('/esg/create', data),
|
|
||||||
updateESG: (data = {}) => request.post('/esg/update', data),
|
|
||||||
deleteESG: (params = {}) => request.delete('/esg/delete', { params }),
|
|
||||||
// index
|
|
||||||
getIndexList: (params = {}) => request.get('/index/list', { params }),
|
|
||||||
getIndexById: (params = {}) => request.get('/index/get', { params }),
|
|
||||||
createIndex: (data = {}) => request.post('/index/create', data),
|
|
||||||
updateIndex: (data = {}) => request.post('/index/update', data),
|
|
||||||
deleteIndex: (params = {}) => request.delete('/index/delete', { params }),
|
|
||||||
// industry
|
|
||||||
getIndustryList: (params = {}) => request.get('/industry/list', { params }),
|
|
||||||
getIndustryById: (params = {}) => request.get('/industry/get', { params }),
|
|
||||||
createIndustry: (data = {}) => request.post('/industry/create', data),
|
|
||||||
updateIndustry: (data = {}) => request.post('/industry/update', data),
|
|
||||||
deleteIndustry: (params = {}) => request.delete('/industry/delete', { params }),
|
|
||||||
// policy
|
|
||||||
getPolicyList: (params = {}) => request.get('/policy/list', { params }),
|
|
||||||
getPolicyById: (params = {}) => request.get('/policy/get', { params }),
|
|
||||||
createPolicy: (data = {}) => request.post('/policy/create', data),
|
|
||||||
updatePolicy: (data = {}) => request.post('/policy/update', data),
|
|
||||||
deletePolicy: (params = {}) => request.delete('/policy/delete', { params }),
|
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 642 B |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 472 B |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 662 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
@ -6,7 +6,7 @@ const Layout = () => import('@/layout/index.vue')
|
|||||||
export const basicRoutes = [
|
export const basicRoutes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: '/pages', // 默认跳转到首页
|
redirect: '/workbench', // 默认跳转到首页
|
||||||
meta: { order: 0 },
|
meta: { order: 0 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -95,12 +95,6 @@ export const basicRoutes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'pages',
|
|
||||||
path: '/pages',
|
|
||||||
component: () => import('@/views/pages/index.vue'),
|
|
||||||
isHidden: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: '403',
|
name: '403',
|
||||||
path: '/403',
|
path: '/403',
|
||||||
|
|||||||
@ -77,13 +77,13 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async generateRoutes() {
|
async generateRoutes() {
|
||||||
// const res = await api.getUserMenu() // 调用接口获取后端传来的菜单路由
|
const res = await api.getUserMenu() // 调用接口获取后端传来的菜单路由
|
||||||
// this.accessRoutes = buildRoutes(res.data) // 处理成前端路由格式
|
this.accessRoutes = buildRoutes(res.data) // 处理成前端路由格式
|
||||||
return this.accessRoutes
|
return this.accessRoutes
|
||||||
},
|
},
|
||||||
async getAccessApis() {
|
async getAccessApis() {
|
||||||
// const res = await api.getUserApi()
|
const res = await api.getUserApi()
|
||||||
// this.accessApis = res.data
|
this.accessApis = res.data
|
||||||
return this.accessApis
|
return this.accessApis
|
||||||
},
|
},
|
||||||
resetPermission() {
|
resetPermission() {
|
||||||
|
|||||||
@ -36,15 +36,14 @@ export const useUserStore = defineStore('user', {
|
|||||||
actions: {
|
actions: {
|
||||||
async getUserInfo() {
|
async getUserInfo() {
|
||||||
try {
|
try {
|
||||||
// const res = await api.getUserInfo()
|
const res = await api.getUserInfo()
|
||||||
// if (res.code === 401) {
|
if (res.code === 401) {
|
||||||
// this.logout()
|
this.logout()
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
// const { id, username, email, avatar, roles, is_superuser, is_active } = res.data
|
const { id, username, email, avatar, roles, is_superuser, is_active } = res.data
|
||||||
// this.userInfo = { id, username, email, avatar, roles, is_superuser, is_active }
|
this.userInfo = { id, username, email, avatar, roles, is_superuser, is_active }
|
||||||
// return res.data
|
return res.data
|
||||||
return {}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,5 +16,4 @@ export function createAxios(options = {}) {
|
|||||||
|
|
||||||
export const request = createAxios({
|
export const request = createAxios({
|
||||||
baseURL: import.meta.env.VITE_BASE_API,
|
baseURL: import.meta.env.VITE_BASE_API,
|
||||||
Authorization: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxOCwicGhvbmUiOiIxNTg1MDIwMTEzOSIsImV4cCI6MTc2MDYzNTc0NX0.Z-2oCgVYlLo4JVuFLwNWqhj2iYyAvkZxWQp0h6AlhuI'
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -26,10 +26,7 @@ export function resResolve(response) {
|
|||||||
const code = data?.code ?? status
|
const code = data?.code ?? status
|
||||||
/** 根据code处理对应的操作,并返回处理后的message */
|
/** 根据code处理对应的操作,并返回处理后的message */
|
||||||
const message = resolveResError(code, data?.msg ?? statusText)
|
const message = resolveResError(code, data?.msg ?? statusText)
|
||||||
console.log(message,'message')
|
window.$message?.error(message, { keepAliveOnHover: true })
|
||||||
if(message){
|
|
||||||
window.$message?.error(message, { keepAliveOnHover: true })
|
|
||||||
}
|
|
||||||
return Promise.reject({ code, message, error: data || response })
|
return Promise.reject({ code, message, error: data || response })
|
||||||
}
|
}
|
||||||
return Promise.resolve(data)
|
return Promise.resolve(data)
|
||||||
@ -40,10 +37,7 @@ export async function resReject(error) {
|
|||||||
const code = error?.code
|
const code = error?.code
|
||||||
/** 根据code处理对应的操作,并返回处理后的message */
|
/** 根据code处理对应的操作,并返回处理后的message */
|
||||||
const message = resolveResError(code, error.message)
|
const message = resolveResError(code, error.message)
|
||||||
console.log(message,'message')
|
window.$message?.error(message)
|
||||||
if(message){
|
|
||||||
window.$message?.error(message)
|
|
||||||
}
|
|
||||||
return Promise.reject({ code, message, error })
|
return Promise.reject({ code, message, error })
|
||||||
}
|
}
|
||||||
const { data, status } = error.response
|
const { data, status } = error.response
|
||||||
@ -60,9 +54,6 @@ export async function resReject(error) {
|
|||||||
// 后端返回的response数据
|
// 后端返回的response数据
|
||||||
const code = data?.code ?? status
|
const code = data?.code ?? status
|
||||||
const message = resolveResError(code, data?.msg ?? error.message)
|
const message = resolveResError(code, data?.msg ?? error.message)
|
||||||
console.log(message,'message')
|
window.$message?.error(message, { keepAliveOnHover: true })
|
||||||
if(message){
|
|
||||||
window.$message?.error(message, { keepAliveOnHover: true })
|
|
||||||
}
|
|
||||||
return Promise.reject({ code, message, error: error.response?.data || error.response })
|
return Promise.reject({ code, message, error: error.response?.data || error.response })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,282 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
|
||||||
import {
|
|
||||||
NButton,
|
|
||||||
NForm,
|
|
||||||
NFormItem,
|
|
||||||
NInput,
|
|
||||||
NInputNumber,
|
|
||||||
NSpace,
|
|
||||||
NTag,
|
|
||||||
NPopconfirm,
|
|
||||||
} from 'naive-ui'
|
|
||||||
|
|
||||||
import CommonPage from '@/components/page/CommonPage.vue'
|
|
||||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
|
||||||
import CrudModal from '@/components/table/CrudModal.vue'
|
|
||||||
import CrudTable from '@/components/table/CrudTable.vue'
|
|
||||||
|
|
||||||
import { formatDate, renderIcon } from '@/utils'
|
|
||||||
import { useCRUD } from '@/composables'
|
|
||||||
import api from '@/api'
|
|
||||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ESG关联' })
|
|
||||||
|
|
||||||
const $table = ref(null)
|
|
||||||
const queryItems = ref({})
|
|
||||||
const vPermission = resolveDirective('permission')
|
|
||||||
|
|
||||||
const {
|
|
||||||
modalVisible,
|
|
||||||
modalTitle,
|
|
||||||
modalAction,
|
|
||||||
modalLoading,
|
|
||||||
handleSave,
|
|
||||||
modalForm,
|
|
||||||
modalFormRef,
|
|
||||||
handleEdit,
|
|
||||||
handleDelete,
|
|
||||||
handleAdd,
|
|
||||||
} = useCRUD({
|
|
||||||
name: 'ESG',
|
|
||||||
initForm: { code: '', name: '', level: '', number: '', remark: '' },
|
|
||||||
doCreate: api.createESG,
|
|
||||||
doUpdate: api.updateESG,
|
|
||||||
doDelete: api.deleteESG,
|
|
||||||
refresh: () => $table.value?.handleSearch(),
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
$table.value?.handleSearch()
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'ESG代码',
|
|
||||||
key: 'code',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ESG名称',
|
|
||||||
key: 'name',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ESG级别',
|
|
||||||
key: 'level',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
render(row) {
|
|
||||||
const levelMap = {
|
|
||||||
'E': { type: 'success', text: '环境(E)' },
|
|
||||||
'S': { type: 'info', text: '社会(S)' },
|
|
||||||
'G': { type: 'warning', text: '治理(G)' },
|
|
||||||
'ESG': { type: 'error', text: '综合ESG' }
|
|
||||||
}
|
|
||||||
const level = levelMap[row.level] || { type: 'default', text: row.level }
|
|
||||||
return h(
|
|
||||||
NTag,
|
|
||||||
{ type: level.type },
|
|
||||||
{ default: () => level.text }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ESG编号',
|
|
||||||
key: 'number',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '备注',
|
|
||||||
key: 'remark',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
key: 'created_at',
|
|
||||||
align: 'center',
|
|
||||||
width: 180,
|
|
||||||
render(row) {
|
|
||||||
return h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'text', ghost: true },
|
|
||||||
{
|
|
||||||
default: () => formatDate(row.created_at),
|
|
||||||
icon: renderIcon('mdi:update', { size: 16 }),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
width: 240,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
hideInExcel: true,
|
|
||||||
render(row) {
|
|
||||||
return [
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
type: 'primary',
|
|
||||||
secondary: true,
|
|
||||||
onClick: () => handleEdit(row),
|
|
||||||
},
|
|
||||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
[[vPermission, 'post/api/v1/esg/update']]
|
|
||||||
),
|
|
||||||
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NPopconfirm,
|
|
||||||
{
|
|
||||||
onPositiveClick: () => handleDelete([row.id], false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '确认删除',
|
|
||||||
trigger: () =>
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
|
||||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
[[vPermission, 'delete/api/v1/esg/delete']]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const modalRules = {
|
|
||||||
code: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入ESG代码',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
name: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入ESG名称',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
level: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入ESG级别',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
number: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入ESG编号',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<CommonPage show-footer title="ESG关联">
|
|
||||||
<template #action>
|
|
||||||
<div>
|
|
||||||
<NButton
|
|
||||||
v-permission="'post/api/v1/esg/create'"
|
|
||||||
class="float-right mb-8"
|
|
||||||
type="primary"
|
|
||||||
@click="handleAdd"
|
|
||||||
>
|
|
||||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建ESG
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<CrudTable
|
|
||||||
ref="$table"
|
|
||||||
v-model:query-items="queryItems"
|
|
||||||
:extra-params="{ ordering: 'id' }"
|
|
||||||
:scroll-x="1200"
|
|
||||||
:columns="columns"
|
|
||||||
:get-data="api.getESGList"
|
|
||||||
>
|
|
||||||
<template #queryBar>
|
|
||||||
<QueryBarItem label="ESG代码" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.code"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入ESG代码"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
<QueryBarItem label="ESG名称" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.name"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入ESG名称"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
<QueryBarItem label="ESG级别" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.level"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入ESG级别"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
</template>
|
|
||||||
</CrudTable>
|
|
||||||
|
|
||||||
<!-- 新增/编辑弹窗 -->
|
|
||||||
<CrudModal
|
|
||||||
v-model:visible="modalVisible"
|
|
||||||
:title="modalTitle"
|
|
||||||
:loading="modalLoading"
|
|
||||||
:show-footer="true"
|
|
||||||
@save="handleSave"
|
|
||||||
>
|
|
||||||
<NForm
|
|
||||||
ref="modalFormRef"
|
|
||||||
label-placement="left"
|
|
||||||
label-align="left"
|
|
||||||
:label-width="80"
|
|
||||||
:model="modalForm"
|
|
||||||
:rules="modalRules"
|
|
||||||
>
|
|
||||||
<NFormItem label="ESG代码" path="code">
|
|
||||||
<NInput v-model:value="modalForm.code" placeholder="请输入ESG代码" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="ESG名称" path="name">
|
|
||||||
<NInput v-model:value="modalForm.name" placeholder="请输入ESG名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="ESG级别" path="level">
|
|
||||||
<NInput v-model:value="modalForm.level" placeholder="请输入ESG级别(如:E, S, G, ESG)" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="ESG编号" path="number">
|
|
||||||
<NInput v-model:value="modalForm.number" placeholder="请输入ESG编号" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="备注" path="remark">
|
|
||||||
<NInput v-model:value="modalForm.remark" type="textarea" placeholder="请输入备注" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</CrudModal>
|
|
||||||
</CommonPage>
|
|
||||||
</template>
|
|
||||||
@ -1,257 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
|
||||||
import {
|
|
||||||
NButton,
|
|
||||||
NForm,
|
|
||||||
NFormItem,
|
|
||||||
NInput,
|
|
||||||
NInputNumber,
|
|
||||||
NSpace,
|
|
||||||
NTag,
|
|
||||||
NPopconfirm,
|
|
||||||
} from 'naive-ui'
|
|
||||||
|
|
||||||
import CommonPage from '@/components/page/CommonPage.vue'
|
|
||||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
|
||||||
import CrudModal from '@/components/table/CrudModal.vue'
|
|
||||||
import CrudTable from '@/components/table/CrudTable.vue'
|
|
||||||
|
|
||||||
import { formatDate, renderIcon } from '@/utils'
|
|
||||||
import { useCRUD } from '@/composables'
|
|
||||||
import api from '@/api'
|
|
||||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: '行业基准' })
|
|
||||||
|
|
||||||
const $table = ref(null)
|
|
||||||
const queryItems = ref({})
|
|
||||||
const vPermission = resolveDirective('permission')
|
|
||||||
|
|
||||||
const {
|
|
||||||
modalVisible,
|
|
||||||
modalTitle,
|
|
||||||
modalAction,
|
|
||||||
modalLoading,
|
|
||||||
handleSave,
|
|
||||||
modalForm,
|
|
||||||
modalFormRef,
|
|
||||||
handleEdit,
|
|
||||||
handleDelete,
|
|
||||||
handleAdd,
|
|
||||||
} = useCRUD({
|
|
||||||
name: '指数',
|
|
||||||
initForm: { code: '', name: '', search_num: 0, remark: '' },
|
|
||||||
doCreate: api.createIndex,
|
|
||||||
doUpdate: api.updateIndex,
|
|
||||||
doDelete: api.deleteIndex,
|
|
||||||
refresh: () => $table.value?.handleSearch(),
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
$table.value?.handleSearch()
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '指数代码',
|
|
||||||
key: 'code',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '指数名称',
|
|
||||||
key: 'name',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '搜索次数',
|
|
||||||
key: 'search_num',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
render(row) {
|
|
||||||
const getSearchType = (num) => {
|
|
||||||
if (num >= 1000) return 'error'
|
|
||||||
if (num >= 500) return 'warning'
|
|
||||||
if (num >= 100) return 'info'
|
|
||||||
return 'default'
|
|
||||||
}
|
|
||||||
return h(
|
|
||||||
NTag,
|
|
||||||
{ type: getSearchType(row.search_num) },
|
|
||||||
{ default: () => `${row.search_num}次` }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '备注',
|
|
||||||
key: 'remark',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
key: 'created_at',
|
|
||||||
align: 'center',
|
|
||||||
width: 180,
|
|
||||||
render(row) {
|
|
||||||
return h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'text', ghost: true },
|
|
||||||
{
|
|
||||||
default: () => formatDate(row.created_at),
|
|
||||||
icon: renderIcon('mdi:update', { size: 16 }),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
width: 240,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
hideInExcel: true,
|
|
||||||
render(row) {
|
|
||||||
return [
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
type: 'primary',
|
|
||||||
secondary: true,
|
|
||||||
onClick: () => handleEdit(row),
|
|
||||||
},
|
|
||||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
[[vPermission, 'post/api/v1/index/update']]
|
|
||||||
),
|
|
||||||
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NPopconfirm,
|
|
||||||
{
|
|
||||||
onPositiveClick: () => handleDelete([row.id], false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '确认删除',
|
|
||||||
trigger: () =>
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
|
||||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
[[vPermission, 'delete/api/v1/index/delete']]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const modalRules = {
|
|
||||||
code: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入指数代码',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
name: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入指数名称',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
search_num: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
type: 'number',
|
|
||||||
message: '请输入搜索次数',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<CommonPage show-footer title="行业基准">
|
|
||||||
<template #action>
|
|
||||||
<div>
|
|
||||||
<NButton
|
|
||||||
v-permission="'post/api/v1/index/create'"
|
|
||||||
class="float-right mb-8"
|
|
||||||
type="primary"
|
|
||||||
@click="handleAdd"
|
|
||||||
>
|
|
||||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建指数
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<CrudTable
|
|
||||||
ref="$table"
|
|
||||||
v-model:query-items="queryItems"
|
|
||||||
:extra-params="{ ordering: 'id' }"
|
|
||||||
:scroll-x="1200"
|
|
||||||
:columns="columns"
|
|
||||||
:get-data="api.getIndexList"
|
|
||||||
>
|
|
||||||
<template #queryBar>
|
|
||||||
<QueryBarItem label="指数代码" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.code"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入指数代码"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
<QueryBarItem label="指数名称" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.name"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入指数名称"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
</template>
|
|
||||||
</CrudTable>
|
|
||||||
|
|
||||||
<!-- 新增/编辑弹窗 -->
|
|
||||||
<CrudModal
|
|
||||||
v-model:visible="modalVisible"
|
|
||||||
:title="modalTitle"
|
|
||||||
:loading="modalLoading"
|
|
||||||
:show-footer="true"
|
|
||||||
@save="handleSave"
|
|
||||||
>
|
|
||||||
<NForm
|
|
||||||
ref="modalFormRef"
|
|
||||||
label-placement="left"
|
|
||||||
label-align="left"
|
|
||||||
:label-width="80"
|
|
||||||
:model="modalForm"
|
|
||||||
:rules="modalRules"
|
|
||||||
>
|
|
||||||
<NFormItem label="指数代码" path="code">
|
|
||||||
<NInput v-model:value="modalForm.code" placeholder="请输入指数代码" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="指数名称" path="name">
|
|
||||||
<NInput v-model:value="modalForm.name" placeholder="请输入指数名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="搜索次数" path="search_num">
|
|
||||||
<NInputNumber v-model:value="modalForm.search_num" placeholder="请输入搜索次数" :min="0" :precision="0" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="备注" path="remark">
|
|
||||||
<NInput v-model:value="modalForm.remark" type="textarea" placeholder="请输入备注" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</CrudModal>
|
|
||||||
</CommonPage>
|
|
||||||
</template>
|
|
||||||
@ -1,275 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
|
||||||
import {
|
|
||||||
NButton,
|
|
||||||
NForm,
|
|
||||||
NFormItem,
|
|
||||||
NInput,
|
|
||||||
NInputNumber,
|
|
||||||
NSpace,
|
|
||||||
NTag,
|
|
||||||
NPopconfirm,
|
|
||||||
} from 'naive-ui'
|
|
||||||
|
|
||||||
import CommonPage from '@/components/page/CommonPage.vue'
|
|
||||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
|
||||||
import CrudModal from '@/components/table/CrudModal.vue'
|
|
||||||
import CrudTable from '@/components/table/CrudTable.vue'
|
|
||||||
|
|
||||||
import { formatDate, renderIcon } from '@/utils'
|
|
||||||
import { useCRUD } from '@/composables'
|
|
||||||
import api from '@/api'
|
|
||||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: '行业修正' })
|
|
||||||
|
|
||||||
const $table = ref(null)
|
|
||||||
const queryItems = ref({})
|
|
||||||
const vPermission = resolveDirective('permission')
|
|
||||||
|
|
||||||
const {
|
|
||||||
modalVisible,
|
|
||||||
modalTitle,
|
|
||||||
modalAction,
|
|
||||||
modalLoading,
|
|
||||||
handleSave,
|
|
||||||
modalForm,
|
|
||||||
modalFormRef,
|
|
||||||
handleEdit,
|
|
||||||
handleDelete,
|
|
||||||
handleAdd,
|
|
||||||
} = useCRUD({
|
|
||||||
name: '行业',
|
|
||||||
initForm: { code: '', name: '', roe: 0, fix_num: 0, remark: '' },
|
|
||||||
doCreate: api.createIndustry,
|
|
||||||
doUpdate: api.updateIndustry,
|
|
||||||
doDelete: api.deleteIndustry,
|
|
||||||
refresh: () => $table.value?.handleSearch(),
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
$table.value?.handleSearch()
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '行业代码',
|
|
||||||
key: 'code',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '行业名称',
|
|
||||||
key: 'name',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ROE',
|
|
||||||
key: 'roe',
|
|
||||||
width: 100,
|
|
||||||
align: 'center',
|
|
||||||
render(row) {
|
|
||||||
return h(
|
|
||||||
NTag,
|
|
||||||
{ type: 'info' },
|
|
||||||
{ default: () => `${row.roe}%` }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '修正数值',
|
|
||||||
key: 'fix_num',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
render(row) {
|
|
||||||
return h(
|
|
||||||
NTag,
|
|
||||||
{ type: row.fix_num > 0 ? 'success' : row.fix_num < 0 ? 'error' : 'default' },
|
|
||||||
{ default: () => row.fix_num }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '备注',
|
|
||||||
key: 'remark',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
key: 'created_at',
|
|
||||||
align: 'center',
|
|
||||||
width: 180,
|
|
||||||
render(row) {
|
|
||||||
return h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'text', ghost: true },
|
|
||||||
{
|
|
||||||
default: () => formatDate(row.created_at),
|
|
||||||
icon: renderIcon('mdi:update', { size: 16 }),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
width: 240,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
hideInExcel: true,
|
|
||||||
render(row) {
|
|
||||||
return [
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
type: 'primary',
|
|
||||||
secondary: true,
|
|
||||||
onClick: () => handleEdit(row),
|
|
||||||
},
|
|
||||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
[[vPermission, 'post/api/v1/industry/update']]
|
|
||||||
),
|
|
||||||
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NPopconfirm,
|
|
||||||
{
|
|
||||||
onPositiveClick: () => handleDelete([row.id], false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '确认删除',
|
|
||||||
trigger: () =>
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
|
||||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
[[vPermission, 'delete/api/v1/industry/delete']]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const modalRules = {
|
|
||||||
code: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入行业代码',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
name: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入行业名称',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
roe: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
type: 'number',
|
|
||||||
message: '请输入ROE值',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fix_num: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
type: 'number',
|
|
||||||
message: '请输入修正数值',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<CommonPage show-footer title="行业修正">
|
|
||||||
<template #action>
|
|
||||||
<div>
|
|
||||||
<NButton
|
|
||||||
v-permission="'post/api/v1/industry/create'"
|
|
||||||
class="float-right mb-8"
|
|
||||||
type="primary"
|
|
||||||
@click="handleAdd"
|
|
||||||
>
|
|
||||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建行业
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<CrudTable
|
|
||||||
ref="$table"
|
|
||||||
v-model:query-items="queryItems"
|
|
||||||
:extra-params="{ ordering: 'id' }"
|
|
||||||
:scroll-x="1200"
|
|
||||||
:columns="columns"
|
|
||||||
:get-data="api.getIndustryList"
|
|
||||||
>
|
|
||||||
<template #queryBar>
|
|
||||||
<QueryBarItem label="行业代码" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.code"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入行业代码"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
<QueryBarItem label="行业名称" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.name"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入行业名称"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
</template>
|
|
||||||
</CrudTable>
|
|
||||||
|
|
||||||
<!-- 新增/编辑弹窗 -->
|
|
||||||
<CrudModal
|
|
||||||
v-model:visible="modalVisible"
|
|
||||||
:title="modalTitle"
|
|
||||||
:loading="modalLoading"
|
|
||||||
:show-footer="true"
|
|
||||||
@save="handleSave"
|
|
||||||
>
|
|
||||||
<NForm
|
|
||||||
ref="modalFormRef"
|
|
||||||
label-placement="left"
|
|
||||||
label-align="left"
|
|
||||||
:label-width="80"
|
|
||||||
:model="modalForm"
|
|
||||||
:rules="modalRules"
|
|
||||||
>
|
|
||||||
<NFormItem label="行业代码" path="code">
|
|
||||||
<NInput v-model:value="modalForm.code" placeholder="请输入行业代码" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="行业名称" path="name">
|
|
||||||
<NInput v-model:value="modalForm.name" placeholder="请输入行业名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="ROE" path="roe">
|
|
||||||
<NInputNumber v-model:value="modalForm.roe" placeholder="请输入ROE值" :precision="2" :step="0.01" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="修正数值" path="fix_num">
|
|
||||||
<NInputNumber v-model:value="modalForm.fix_num" placeholder="请输入修正数值" :precision="2" :step="0.01" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="备注" path="remark">
|
|
||||||
<NInput v-model:value="modalForm.remark" type="textarea" placeholder="请输入备注" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</CrudModal>
|
|
||||||
</CommonPage>
|
|
||||||
</template>
|
|
||||||
@ -1,284 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
|
||||||
import {
|
|
||||||
NButton,
|
|
||||||
NForm,
|
|
||||||
NFormItem,
|
|
||||||
NInput,
|
|
||||||
NInputNumber,
|
|
||||||
NSpace,
|
|
||||||
NTag,
|
|
||||||
NPopconfirm,
|
|
||||||
} from 'naive-ui'
|
|
||||||
|
|
||||||
import CommonPage from '@/components/page/CommonPage.vue'
|
|
||||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
|
||||||
import CrudModal from '@/components/table/CrudModal.vue'
|
|
||||||
import CrudTable from '@/components/table/CrudTable.vue'
|
|
||||||
|
|
||||||
import { formatDate, renderIcon } from '@/utils'
|
|
||||||
import { useCRUD } from '@/composables'
|
|
||||||
import api from '@/api'
|
|
||||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: '政策匹配' })
|
|
||||||
|
|
||||||
const $table = ref(null)
|
|
||||||
const queryItems = ref({})
|
|
||||||
const vPermission = resolveDirective('permission')
|
|
||||||
|
|
||||||
const {
|
|
||||||
modalVisible,
|
|
||||||
modalTitle,
|
|
||||||
modalAction,
|
|
||||||
modalLoading,
|
|
||||||
handleSave,
|
|
||||||
modalForm,
|
|
||||||
modalFormRef,
|
|
||||||
handleEdit,
|
|
||||||
handleDelete,
|
|
||||||
handleAdd,
|
|
||||||
} = useCRUD({
|
|
||||||
name: '政策',
|
|
||||||
initForm: { code: '', name: '', level: '', score: 0 },
|
|
||||||
doCreate: api.createPolicy,
|
|
||||||
doUpdate: api.updatePolicy,
|
|
||||||
doDelete: api.deletePolicy,
|
|
||||||
refresh: () => $table.value?.handleSearch(),
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
$table.value?.handleSearch()
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '政策代码',
|
|
||||||
key: 'code',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '政策名称',
|
|
||||||
key: 'name',
|
|
||||||
width: 200,
|
|
||||||
align: 'center',
|
|
||||||
ellipsis: { tooltip: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '政策级别',
|
|
||||||
key: 'level',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
render(row) {
|
|
||||||
const levelMap = {
|
|
||||||
'national': { type: 'error', text: '国家级' },
|
|
||||||
'provincial': { type: 'warning', text: '省级' },
|
|
||||||
'municipal': { type: 'info', text: '市级' },
|
|
||||||
'county': { type: 'default', text: '县级' }
|
|
||||||
}
|
|
||||||
const level = levelMap[row.level] || { type: 'default', text: row.level }
|
|
||||||
return h(
|
|
||||||
NTag,
|
|
||||||
{ type: level.type },
|
|
||||||
{ default: () => level.text }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '政策评分',
|
|
||||||
key: 'score',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
render(row) {
|
|
||||||
const getScoreType = (score) => {
|
|
||||||
if (score >= 80) return 'success'
|
|
||||||
if (score >= 60) return 'warning'
|
|
||||||
return 'error'
|
|
||||||
}
|
|
||||||
return h(
|
|
||||||
NTag,
|
|
||||||
{ type: getScoreType(row.score) },
|
|
||||||
{ default: () => `${row.score}分` }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
key: 'created_at',
|
|
||||||
align: 'center',
|
|
||||||
width: 180,
|
|
||||||
render(row) {
|
|
||||||
return h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'text', ghost: true },
|
|
||||||
{
|
|
||||||
default: () => formatDate(row.created_at),
|
|
||||||
icon: renderIcon('mdi:update', { size: 16 }),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
width: 240,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
hideInExcel: true,
|
|
||||||
render(row) {
|
|
||||||
return [
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
type: 'primary',
|
|
||||||
secondary: true,
|
|
||||||
onClick: () => handleEdit(row),
|
|
||||||
},
|
|
||||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
[[vPermission, 'post/api/v1/policy/update']]
|
|
||||||
),
|
|
||||||
|
|
||||||
withDirectives(
|
|
||||||
h(
|
|
||||||
NPopconfirm,
|
|
||||||
{
|
|
||||||
onPositiveClick: () => handleDelete([row.id], false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '确认删除',
|
|
||||||
trigger: () =>
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
|
||||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
[[vPermission, 'delete/api/v1/policy/delete']]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const modalRules = {
|
|
||||||
code: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入政策代码',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
name: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入政策名称',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
level: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入政策级别',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
score: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
type: 'number',
|
|
||||||
message: '请输入政策评分',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<CommonPage show-footer title="政策匹配">
|
|
||||||
<template #action>
|
|
||||||
<div>
|
|
||||||
<NButton
|
|
||||||
v-permission="'post/api/v1/policy/create'"
|
|
||||||
class="float-right mb-8"
|
|
||||||
type="primary"
|
|
||||||
@click="handleAdd"
|
|
||||||
>
|
|
||||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建政策
|
|
||||||
</NButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<CrudTable
|
|
||||||
ref="$table"
|
|
||||||
v-model:query-items="queryItems"
|
|
||||||
:extra-params="{ ordering: 'id' }"
|
|
||||||
:scroll-x="1200"
|
|
||||||
:columns="columns"
|
|
||||||
:get-data="api.getPolicyList"
|
|
||||||
>
|
|
||||||
<template #queryBar>
|
|
||||||
<QueryBarItem label="政策代码" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.code"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入政策代码"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
<QueryBarItem label="政策名称" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.name"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入政策名称"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
<QueryBarItem label="政策级别" :label-width="80">
|
|
||||||
<NInput
|
|
||||||
v-model:value="queryItems.level"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入政策级别"
|
|
||||||
@keydown.enter="$table?.handleSearch"
|
|
||||||
/>
|
|
||||||
</QueryBarItem>
|
|
||||||
</template>
|
|
||||||
</CrudTable>
|
|
||||||
|
|
||||||
<!-- 新增/编辑弹窗 -->
|
|
||||||
<CrudModal
|
|
||||||
v-model:visible="modalVisible"
|
|
||||||
:title="modalTitle"
|
|
||||||
:loading="modalLoading"
|
|
||||||
:show-footer="true"
|
|
||||||
@save="handleSave"
|
|
||||||
>
|
|
||||||
<NForm
|
|
||||||
ref="modalFormRef"
|
|
||||||
label-placement="left"
|
|
||||||
label-align="left"
|
|
||||||
:label-width="80"
|
|
||||||
:model="modalForm"
|
|
||||||
:rules="modalRules"
|
|
||||||
>
|
|
||||||
<NFormItem label="政策代码" path="code">
|
|
||||||
<NInput v-model:value="modalForm.code" placeholder="请输入政策代码" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="政策名称" path="name">
|
|
||||||
<NInput v-model:value="modalForm.name" placeholder="请输入政策名称" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="政策级别" path="level">
|
|
||||||
<NInput v-model:value="modalForm.level" placeholder="请输入政策级别(如:national, provincial, municipal, county)" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem label="政策评分" path="score">
|
|
||||||
<NInputNumber v-model:value="modalForm.score" placeholder="请输入政策评分" :min="0" :max="100" :precision="0" />
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
</CrudModal>
|
|
||||||
</CommonPage>
|
|
||||||
</template>
|
|
||||||
@ -2,40 +2,49 @@
|
|||||||
<AppPage :show-footer="true" bg-cover :style="{ backgroundImage: `url(${bgImg})` }">
|
<AppPage :show-footer="true" bg-cover :style="{ backgroundImage: `url(${bgImg})` }">
|
||||||
<div
|
<div
|
||||||
style="transform: translateY(25px)"
|
style="transform: translateY(25px)"
|
||||||
class="m-auto max-w-1500 min-w-750 f-c-c rounded-12 bg-white bg-opacity-80"
|
class="m-auto max-w-1500 min-w-345 f-c-c rounded-10 bg-white bg-opacity-60 p-15 card-shadow"
|
||||||
dark:bg-dark
|
dark:bg-dark
|
||||||
>
|
>
|
||||||
<div w-750 px-20 style="height: 400px; padding-top: 50px; text-align: center;">
|
<div hidden w-380 px-20 py-35 md:block>
|
||||||
<img style="width: 371px; height: 60px; margin: auto;" src="@/assets/images/logo.png" alt="">
|
<icon-custom-front-page pt-10 text-300 color-primary></icon-custom-front-page>
|
||||||
<div mt-50 style="text-align: center; font-size: 48px; color: #303133; line-height: 48px; font-weight: 600; ">
|
</div>
|
||||||
非遗IP价值评估系统
|
|
||||||
</div>
|
<div w-320 flex-col px-20 py-35>
|
||||||
<div style="text-align: center; margin-top: 16px; color: #606266;">
|
<h5 f-c-c text-24 font-normal color="#6a6a6a">
|
||||||
基于深度学习算法的智能评估系统,为您的知识产权和非物质文化遗产提供专业的价值评估服务
|
<icon-custom-logo mr-10 text-50 color-primary />{{ $t('app_name') }}
|
||||||
|
</h5>
|
||||||
|
<div mt-30>
|
||||||
|
<n-input
|
||||||
|
v-model:value="loginInfo.username"
|
||||||
|
autofocus
|
||||||
|
class="h-50 items-center pl-10 text-16"
|
||||||
|
placeholder="admin"
|
||||||
|
:maxlength="20"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div mt-30>
|
<div mt-30>
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="loginInfo.phone"
|
v-model:value="loginInfo.password"
|
||||||
style="display: inline-block; width: 260px; height: 42px; text-align: left; line-height: 42px;"
|
class="h-50 items-center pl-10 text-16"
|
||||||
placeholder="请输入手机号"
|
type="password"
|
||||||
|
show-password-on="mousedown"
|
||||||
|
placeholder="123456"
|
||||||
:maxlength="20"
|
:maxlength="20"
|
||||||
@keypress.enter="handleRegister"
|
@keypress.enter="handleLogin"
|
||||||
>
|
/>
|
||||||
<template #prefix>
|
</div>
|
||||||
<img style="width: 18px; height: 18px; margin-right: 8px;" src="@/assets/images/phone.png" alt="">
|
|
||||||
</template>
|
|
||||||
</n-input>
|
|
||||||
|
|
||||||
|
<div mt-20>
|
||||||
<n-button
|
<n-button
|
||||||
w-126
|
h-50
|
||||||
h-42
|
w-full
|
||||||
rounded-5
|
rounded-5
|
||||||
|
text-16
|
||||||
type="primary"
|
type="primary"
|
||||||
style="background: linear-gradient( 93deg, #880C22 0%, #A30113 100%); margin-left: 20px; font-size: 16px; border: none !important;"
|
:loading="loading"
|
||||||
@click="handleRegister"
|
@click="handleLogin"
|
||||||
>
|
>
|
||||||
立即登录
|
{{ $t('views.login.text_login') }}
|
||||||
<img style="width: 18px; height: 18px; margin-left: 2px;" src="@/assets/images/go.png" alt="">
|
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +54,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { lStorage, setToken } from '@/utils'
|
import { lStorage, setToken } from '@/utils'
|
||||||
import bgImg from '@/assets/images/login_bg.png'
|
import bgImg from '@/assets/images/login_bg.webp'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { addDynamicRoutes } from '@/router'
|
import { addDynamicRoutes } from '@/router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
@ -55,55 +64,45 @@ const { query } = useRoute()
|
|||||||
const { t } = useI18n({ useScope: 'global' })
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const loginInfo = ref({
|
const loginInfo = ref({
|
||||||
phone: '',
|
username: '',
|
||||||
|
password: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
initLoginInfo()
|
initLoginInfo()
|
||||||
|
|
||||||
function initLoginInfo() {
|
function initLoginInfo() {
|
||||||
if(localStorage.getItem('phone')){
|
const localLoginInfo = lStorage.get('loginInfo')
|
||||||
loginInfo.value.phone = localStorage.getItem('phone')
|
if (localLoginInfo) {
|
||||||
|
loginInfo.value.username = localLoginInfo.username || ''
|
||||||
|
loginInfo.value.password = localLoginInfo.password || ''
|
||||||
}
|
}
|
||||||
// const localLoginInfo = lStorage.get('loginInfo')
|
|
||||||
// if (localLoginInfo) {
|
|
||||||
// loginInfo.value.phone = localLoginInfo.phone || ''
|
|
||||||
// loginInfo.value.password = localLoginInfo.password || ''
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
async function handleLogin() {
|
||||||
async function handleRegister() {
|
const { username, password } = loginInfo.value
|
||||||
const { phone } = loginInfo.value
|
if (!username || !password) {
|
||||||
if (!phone) {
|
$message.warning(t('views.login.message_input_username_password'))
|
||||||
$message.warning('请输入手机号')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
loading.value = true
|
try {
|
||||||
await api.registerPhone({ phone })
|
loading.value = true
|
||||||
.then(res=>{
|
$message.loading(t('views.login.message_verifying'))
|
||||||
handleLogin()
|
const res = await api.login({ username, password: password.toString() })
|
||||||
})
|
$message.success(t('views.login.message_login_success'))
|
||||||
.catch(res=>{
|
setToken(res.data.access_token)
|
||||||
handleLogin()
|
await addDynamicRoutes()
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleLogin() {
|
|
||||||
const { phone } = loginInfo.value
|
|
||||||
loading.value = true
|
|
||||||
await api.loginPhone({ phone, password: phone.slice(5,11) }).catch(res=>{
|
|
||||||
setToken(res.error.access_token)
|
|
||||||
if (query.redirect) {
|
if (query.redirect) {
|
||||||
const path = query.redirect
|
const path = query.redirect
|
||||||
localStorage.setItem('phone', phone)
|
console.log('path', { path, query })
|
||||||
Reflect.deleteProperty(query, 'redirect')
|
Reflect.deleteProperty(query, 'redirect')
|
||||||
router.push({ path, query })
|
router.push({ path, query })
|
||||||
} else {
|
} else {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
}
|
}
|
||||||
loading.value = false
|
} catch (e) {
|
||||||
})
|
console.error('login error', e.error)
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,149 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="step-progress-bar">
|
|
||||||
<div class="step-container">
|
|
||||||
<div
|
|
||||||
v-for="(step, index) in steps"
|
|
||||||
:key="index"
|
|
||||||
class="step-item"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="step-circle"
|
|
||||||
:class="{
|
|
||||||
'completed': currentStep > index,
|
|
||||||
'current': currentStep === index,
|
|
||||||
'pending': currentStep < index
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span>{{ index + 1 }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="step-title">{{ step.title }}</div>
|
|
||||||
|
|
||||||
<!-- 虚线连接线 -->
|
|
||||||
<div v-if="index < steps.length - 1" class="step-line">
|
|
||||||
<div
|
|
||||||
class="line-progress"
|
|
||||||
:style="{ width: lineProgress(index) }"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { PropType } from 'vue';
|
|
||||||
|
|
||||||
interface Step {
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
steps: {
|
|
||||||
type: Array as PropType<Step[]>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
currentStep: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
validator: (val: number) => val >= 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算连接线的进度
|
|
||||||
const lineProgress = (index: number) => {
|
|
||||||
if (props.currentStep > index + 1) {
|
|
||||||
return '100%';
|
|
||||||
} else if (props.currentStep === index + 1) {
|
|
||||||
return '50%';
|
|
||||||
}
|
|
||||||
return '0%';
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.step-progress-bar {
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-circle {
|
|
||||||
z-index: 1;
|
|
||||||
display: flex;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 50%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.completed {
|
|
||||||
color: #fff;
|
|
||||||
background: #A30113;
|
|
||||||
}
|
|
||||||
|
|
||||||
.current {
|
|
||||||
color: #fff;
|
|
||||||
background: #A30113;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pending {
|
|
||||||
color: #000;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
/* border: 1px solid #d9d9d9; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-icon {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-title {
|
|
||||||
margin-top: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-line {
|
|
||||||
position: absolute;
|
|
||||||
top: 16px;
|
|
||||||
left: 50%;
|
|
||||||
z-index: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-progress {
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
transition: width 1s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 虚线背景 */
|
|
||||||
.step-line::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 15%;
|
|
||||||
width: 70%;
|
|
||||||
height: 2px;
|
|
||||||
background-image: linear-gradient(to right, #A30113 50%, transparent 50%);
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
background-size: 9px 3px;
|
|
||||||
content: '';
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -34,7 +34,7 @@
|
|||||||
:key="i"
|
:key="i"
|
||||||
class="mb-10 mt-10 w-300 cursor-pointer"
|
class="mb-10 mt-10 w-300 cursor-pointer"
|
||||||
hover:card-shadow
|
hover:card-shadow
|
||||||
title=""
|
title="Vue FastAPI Admin"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<p op-60>{{ dummyText }}</p>
|
<p op-60>{{ dummyText }}</p>
|
||||||
|
|||||||