feat(ruoyi-ui-next): complete testing and optimization

- Fix 48 TypeScript errors across all view and component files
- Fix SCSS :global syntax causing build failures
- Update vite.config.ts port to 5173
- Add global type declarations for missing modules
- Fix API response type access patterns (res.data.rows)
- Fix Element Plus component type mismatches
- Update README with project documentation
- Run Prettier code formatting
feature/20260628/refactor-frontend-modernization
Lxy 3 weeks ago
parent b6dbf000e0
commit f4b034970b

@ -1,5 +1,71 @@
# Vue 3 + TypeScript + Vite
# RuoYi-Vue 前端Vue3 版本)
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
基于 Vue3 + Vite + TypeScript + Element Plus 的现代化前端架构。
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
## 技术栈
- Vue 3.5+
- Vite 8+
- TypeScript 6+
- Element Plus 2.14+
- Pinia 3+
- Vue Router 4+
- ECharts 6+
- Axios 1.18+
## 开发
```bash
npm install
npm run dev
```
访问 http://localhost:5173
## 构建
```bash
npm run build
```
构建输出位于 `dist/` 目录。
## 特性
- Vue3 Composition API
- TypeScript 严格模式
- 深色主题GitHub Dark 风格)
- 数据可视化ECharts
- 权限控制(指令 + 路由守卫)
- 响应式布局
- 动态路由加载
- 标签页导航
- 代码分割优化
## 项目结构
```
src/
├── api/ # API 接口
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── business/ # 业务组件
│ ├── layout/ # 布局组件
│ └── ui/ # UI 组件
├── composables/ # 组合式函数
├── directives/ # 自定义指令
├── layout/ # 主布局
├── router/ # 路由配置
├── stores/ # Pinia 状态管理
├── styles/ # 全局样式
├── types/ # TypeScript 类型定义
├── utils/ # 工具函数
└── views/ # 页面组件
```
## 构建优化
- Vendor chunk 分离vue-vendor, element-plus, echarts
- 路由懒加载
- 组件按需加载AutoImport + Components
- CSS 预处理器全局变量注入

@ -10,7 +10,8 @@
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

@ -47,7 +47,11 @@ export function updateUserProfile(data: any) {
// 用户密码重置
export function updateUserPwd(oldPassword: string, newPassword: string) {
return request({ url: '/system/user/profile/updatePwd', method: 'put', params: { oldPassword, newPassword } })
return request({
url: '/system/user/profile/updatePwd',
method: 'put',
params: { oldPassword, newPassword },
})
}
// 用户头像上传

@ -85,6 +85,24 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
export type {
Component,
Slot,
Slots,
ComponentPublicInstance,
ComputedRef,
DirectiveBinding,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey,
PropType,
Ref,
ShallowRef,
MaybeRef,
MaybeRefOrGetter,
VNode,
WritableComputedRef,
} from 'vue'
import('vue')
}

@ -47,6 +47,8 @@ declare module 'vue' {
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTransfer: typeof import('element-plus/es')['ElTransfer']

@ -18,9 +18,7 @@ const count = ref(0)
<h1>Get started</h1>
<p>Edit <code>src/App.vue</code> and save to test <code>HMR</code></p>
</div>
<button type="button" class="counter" @click="count++">
Count is {{ count }}
</button>
<button type="button" class="counter" @click="count++">Count is {{ count }}</button>
</section>
<div class="ticks"></div>

@ -1,15 +1,11 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model="radioValue" :value="1">
允许的通配符[, - * ? / L W]
</el-radio>
<el-radio v-model="radioValue" :value="1"> [, - * ? / L W] </el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :value="2">
不指定
</el-radio>
<el-radio v-model="radioValue" :value="2"> </el-radio>
</el-form-item>
<el-form-item>
@ -36,15 +32,19 @@
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :value="6">
本月最后一天
</el-radio>
<el-radio v-model="radioValue" :value="6"> </el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :value="7">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
<el-select
clearable
v-model="checkboxList"
placeholder="可多选"
multiple
style="width: 100%"
>
<el-option v-for="item in 31" :key="item" :value="item">{{ item }}</el-option>
</el-select>
</el-radio>
@ -157,5 +157,14 @@ function setRadioValue(val: number) {
radioValue.value = val
}
defineExpose({ radioValue, checkboxList, workday, cycle01, cycle02, average01, average02, setRadioValue })
defineExpose({
radioValue,
checkboxList,
workday,
cycle01,
cycle02,
average01,
average02,
setRadioValue,
})
</script>

@ -1,9 +1,7 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model="radioValue" :value="1">
小时允许的通配符[, - * /]
</el-radio>
<el-radio v-model="radioValue" :value="1"> [, - * /] </el-radio>
</el-form-item>
<el-form-item>
@ -25,7 +23,13 @@
<el-form-item>
<el-radio v-model="radioValue" :value="4">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
<el-select
clearable
v-model="checkboxList"
placeholder="可多选"
multiple
style="width: 100%"
>
<el-option v-for="item in 24" :key="item" :value="item - 1">{{ item - 1 }}</el-option>
</el-select>
</el-radio>

@ -2,27 +2,15 @@
<div>
<el-tabs type="border-card">
<el-tab-pane label="秒" v-if="shouldHide('second')">
<CrontabSecond
@update="updateCrontabValue"
:check="checkNumber"
ref="cronsecondRef"
/>
<CrontabSecond @update="updateCrontabValue" :check="checkNumber" ref="cronsecondRef" />
</el-tab-pane>
<el-tab-pane label="分钟" v-if="shouldHide('min')">
<CrontabMin
@update="updateCrontabValue"
:check="checkNumber"
ref="cronminRef"
/>
<CrontabMin @update="updateCrontabValue" :check="checkNumber" ref="cronminRef" />
</el-tab-pane>
<el-tab-pane label="小时" v-if="shouldHide('hour')">
<CrontabHour
@update="updateCrontabValue"
:check="checkNumber"
ref="cronhourRef"
/>
<CrontabHour @update="updateCrontabValue" :check="checkNumber" ref="cronhourRef" />
</el-tab-pane>
<el-tab-pane label="日" v-if="shouldHide('day')">
@ -35,11 +23,7 @@
</el-tab-pane>
<el-tab-pane label="月" v-if="shouldHide('month')">
<CrontabMonth
@update="updateCrontabValue"
:check="checkNumber"
ref="cronmonthRef"
/>
<CrontabMonth @update="updateCrontabValue" :check="checkNumber" ref="cronmonthRef" />
</el-tab-pane>
<el-tab-pane label="周" v-if="shouldHide('week')">
@ -52,11 +36,7 @@
</el-tab-pane>
<el-tab-pane label="年" v-if="shouldHide('year')">
<CrontabYear
@update="updateCrontabValue"
:check="checkNumber"
ref="cronyearRef"
/>
<CrontabYear @update="updateCrontabValue" :check="checkNumber" ref="cronyearRef" />
</el-tab-pane>
</el-tabs>
@ -69,14 +49,30 @@
<th>Cron 表达式</th>
</thead>
<tbody>
<td><span>{{ crontabValueObj.second }}</span></td>
<td><span>{{ crontabValueObj.min }}</span></td>
<td><span>{{ crontabValueObj.hour }}</span></td>
<td><span>{{ crontabValueObj.day }}</span></td>
<td><span>{{ crontabValueObj.month }}</span></td>
<td><span>{{ crontabValueObj.week }}</span></td>
<td><span>{{ crontabValueObj.year }}</span></td>
<td><span>{{ crontabValueString }}</span></td>
<td>
<span>{{ crontabValueObj.second }}</span>
</td>
<td>
<span>{{ crontabValueObj.min }}</span>
</td>
<td>
<span>{{ crontabValueObj.hour }}</span>
</td>
<td>
<span>{{ crontabValueObj.day }}</span>
</td>
<td>
<span>{{ crontabValueObj.month }}</span>
</td>
<td>
<span>{{ crontabValueObj.week }}</span>
</td>
<td>
<span>{{ crontabValueObj.year }}</span>
</td>
<td>
<span>{{ crontabValueString }}</span>
</td>
</tbody>
</table>
</div>
@ -110,7 +106,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
expression: '',
hideComponent: () => []
hideComponent: () => [],
})
const emit = defineEmits<{
@ -127,7 +123,7 @@ const crontabValueObj = ref<CrontabValueObj>({
day: '*',
month: '*',
week: '?',
year: ''
year: '',
})
const cronsecondRef = ref<any>(null)
@ -173,7 +169,7 @@ function changeRadio(name: string, value: string) {
cronday: crondayRef.value,
cronmonth: cronmonthRef.value,
cronweek: cronweekRef.value,
cronyear: cronyearRef.value
cronyear: cronyearRef.value,
}
const compRef = refMap[refName]
@ -275,7 +271,7 @@ function resolveExp() {
day: arr[3],
month: arr[4],
week: arr[5],
year: arr[6] || ''
year: arr[6] || '',
}
crontabValueObj.value = { ...obj }
for (const key in obj) {
@ -302,7 +298,7 @@ function clearCron() {
day: '*',
month: '*',
week: '?',
year: ''
year: '',
}
for (const key in crontabValueObj.value) {
changeRadio(key, crontabValueObj.value[key as keyof CrontabValueObj])

@ -1,9 +1,7 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model="radioValue" :value="1">
分钟允许的通配符[, - * /]
</el-radio>
<el-radio v-model="radioValue" :value="1"> [, - * /] </el-radio>
</el-form-item>
<el-form-item>
@ -25,7 +23,13 @@
<el-form-item>
<el-radio v-model="radioValue" :value="4">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
<el-select
clearable
v-model="checkboxList"
placeholder="可多选"
multiple
style="width: 100%"
>
<el-option v-for="item in 60" :key="item" :value="item - 1">{{ item - 1 }}</el-option>
</el-select>
</el-radio>

@ -1,9 +1,7 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model="radioValue" :value="1">
允许的通配符[, - * /]
</el-radio>
<el-radio v-model="radioValue" :value="1"> [, - * /] </el-radio>
</el-form-item>
<el-form-item>
@ -25,7 +23,13 @@
<el-form-item>
<el-radio v-model="radioValue" :value="4">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
<el-select
clearable
v-model="checkboxList"
placeholder="可多选"
multiple
style="width: 100%"
>
<el-option v-for="item in 12" :key="item" :value="item">{{ item }}</el-option>
</el-select>
</el-radio>

@ -62,11 +62,30 @@ function expressionChange() {
let MIdx = getIndex(MDate, nMonth)
let YIdx = getIndex(YDate, nYear)
const resetSecond = () => { sIdx = 0; nSecond = sDate[sIdx] }
const resetMin = () => { mIdx = 0; nMin = mDate[mIdx]; resetSecond() }
const resetHour = () => { hIdx = 0; nHour = hDate[hIdx]; resetMin() }
const resetDay = () => { DIdx = 0; nDay = DDate[DIdx]; resetHour() }
const resetMonth = () => { MIdx = 0; nMonth = MDate[MIdx]; resetDay() }
const resetSecond = () => {
sIdx = 0
nSecond = sDate[sIdx]
}
const resetMin = () => {
mIdx = 0
nMin = mDate[mIdx]
resetSecond()
}
const resetHour = () => {
hIdx = 0
nHour = hDate[hIdx]
resetMin()
}
const resetDay = () => {
DIdx = 0
nDay = DDate[DIdx]
resetHour()
}
const resetMonth = () => {
MIdx = 0
nMonth = MDate[MIdx]
resetDay()
}
if (nYear !== YDate[YIdx]) resetMonth()
if (nMonth !== MDate[MIdx]) resetDay()
@ -76,14 +95,20 @@ function expressionChange() {
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
const YY = YDate[Yi]
if (nMonth > MDate[MDate.length - 1]) { resetMonth(); continue }
if (nMonth > MDate[MDate.length - 1]) {
resetMonth()
continue
}
goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
let MM = MDate[Mi]
const mmStr = MM < 10 ? '0' + MM : String(MM)
if (nDay > DDate[DDate.length - 1]) {
resetDay()
if (Mi === MDate.length - 1) { resetMonth(); continue goYear }
if (Mi === MDate.length - 1) {
resetMonth()
continue goYear
}
continue
}
@ -95,29 +120,48 @@ function expressionChange() {
resetHour()
if (Di === DDate.length - 1) {
resetDay()
if (Mi === MDate.length - 1) { resetMonth(); continue goYear }
if (Mi === MDate.length - 1) {
resetMonth()
continue goYear
}
continue goMonth
}
continue
}
if (checkDate(`${YY}-${mmStr}-${ddStr} 00:00:00`) !== true && dayRule.value !== 'workDay' && dayRule.value !== 'lastWeek' && dayRule.value !== 'lastDay') {
if (
checkDate(`${YY}-${mmStr}-${ddStr} 00:00:00`) !== true &&
dayRule.value !== 'workDay' &&
dayRule.value !== 'lastWeek' &&
dayRule.value !== 'lastDay'
) {
resetDay()
continue goMonth
}
if (dayRule.value === 'lastDay') {
if (checkDate(`${YY}-${mmStr}-${ddStr} 00:00:00`) !== true) {
while (DD > 0 && checkDate(`${YY}-${mmStr}-${(DD < 10 ? '0' + DD : DD)} 00:00:00`) !== true) DD--
while (
DD > 0 &&
checkDate(`${YY}-${mmStr}-${DD < 10 ? '0' + DD : DD} 00:00:00`) !== true
)
DD--
}
} else if (dayRule.value === 'workDay') {
if (checkDate(`${YY}-${mmStr}-${ddStr} 00:00:00`) !== true) {
while (DD > 0 && checkDate(`${YY}-${mmStr}-${(DD < 10 ? '0' + DD : DD)} 00:00:00`) !== true) DD--
while (
DD > 0 &&
checkDate(`${YY}-${mmStr}-${DD < 10 ? '0' + DD : DD} 00:00:00`) !== true
)
DD--
}
const thisWeek = formatDate(new Date(`${YY}-${mmStr}-${(DD < 10 ? '0' + DD : DD)} 00:00:00`), 'week')
const thisWeek = formatDate(
new Date(`${YY}-${mmStr}-${DD < 10 ? '0' + DD : DD} 00:00:00`),
'week'
)
if (thisWeek === 1) {
DD++
if (checkDate(`${YY}-${mmStr}-${(DD < 10 ? '0' + DD : DD)} 00:00:00`) !== true) DD -= 3
if (checkDate(`${YY}-${mmStr}-${DD < 10 ? '0' + DD : DD} 00:00:00`) !== true) DD -= 3
} else if (thisWeek === 7) {
if (dayRuleSup.value !== 1) DD--
else DD += 2
@ -127,7 +171,10 @@ function expressionChange() {
if (!(dayRuleSup.value as number[]).includes(thisWeek)) {
if (Di === DDate.length - 1) {
resetDay()
if (Mi === MDate.length - 1) { resetMonth(); continue goYear }
if (Mi === MDate.length - 1) {
resetMonth()
continue goYear
}
continue goMonth
}
continue
@ -142,9 +189,16 @@ function expressionChange() {
}
} else if (dayRule.value === 'lastWeek') {
if (checkDate(`${YY}-${mmStr}-${ddStr} 00:00:00`) !== true) {
while (DD > 0 && checkDate(`${YY}-${mmStr}-${(DD < 10 ? '0' + DD : DD)} 00:00:00`) !== true) DD--
while (
DD > 0 &&
checkDate(`${YY}-${mmStr}-${DD < 10 ? '0' + DD : DD} 00:00:00`) !== true
)
DD--
}
const thisWeek = formatDate(new Date(`${YY}-${mmStr}-${(DD < 10 ? '0' + DD : DD)} 00:00:00`), 'week')
const thisWeek = formatDate(
new Date(`${YY}-${mmStr}-${DD < 10 ? '0' + DD : DD} 00:00:00`),
'week'
)
const ruleSup = dayRuleSup.value as number
if (ruleSup < thisWeek) DD -= thisWeek - ruleSup
else if (ruleSup > thisWeek) DD -= 7 - (ruleSup - thisWeek)
@ -160,7 +214,10 @@ function expressionChange() {
resetHour()
if (Di === DDate.length - 1) {
resetDay()
if (Mi === MDate.length - 1) { resetMonth(); continue goYear }
if (Mi === MDate.length - 1) {
resetMonth()
continue goYear
}
continue goMonth
}
continue goDay
@ -178,7 +235,10 @@ function expressionChange() {
resetHour()
if (Di === DDate.length - 1) {
resetDay()
if (Mi === MDate.length - 1) { resetMonth(); continue goYear }
if (Mi === MDate.length - 1) {
resetMonth()
continue goYear
}
continue goMonth
}
continue goDay
@ -203,7 +263,10 @@ function expressionChange() {
resetHour()
if (Di === DDate.length - 1) {
resetDay()
if (Mi === MDate.length - 1) { resetMonth(); continue goYear }
if (Mi === MDate.length - 1) {
resetMonth()
continue goYear
}
continue goMonth
}
continue goDay
@ -357,7 +420,7 @@ function getCycleArr(rule: string, limit: number, status: boolean): number[] {
for (let i = min; i <= adjustedMax; i++) {
let add = 0
if (!status && i % limit === 0) add = limit
arr.push(Math.round(i % limit + add))
arr.push(Math.round((i % limit) + add))
}
arr.sort((a, b) => a - b)
return arr

@ -1,9 +1,7 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model="radioValue" :value="1">
允许的通配符[, - * /]
</el-radio>
<el-radio v-model="radioValue" :value="1"> [, - * /] </el-radio>
</el-form-item>
<el-form-item>
@ -25,7 +23,13 @@
<el-form-item>
<el-radio v-model="radioValue" :value="4">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
<el-select
clearable
v-model="checkboxList"
placeholder="可多选"
multiple
style="width: 100%"
>
<el-option v-for="item in 60" :key="item" :value="item - 1">{{ item - 1 }}</el-option>
</el-select>
</el-radio>

@ -1,15 +1,11 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio v-model="radioValue" :value="1">
允许的通配符[, - * ? / L #]
</el-radio>
<el-radio v-model="radioValue" :value="1"> [, - * ? / L #] </el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :value="2">
不指定
</el-radio>
<el-radio v-model="radioValue" :value="2"> </el-radio>
</el-form-item>
<el-form-item>
@ -22,7 +18,8 @@
:label="item.value"
:value="item.key"
:disabled="item.key === 1"
>{{ item.value }}</el-option>
>{{ item.value }}</el-option
>
</el-select>
-
<el-select clearable v-model="cycle02">
@ -32,7 +29,8 @@
:label="item.value"
:value="item.key"
:disabled="item.key < cycle01 && item.key !== 1"
>{{ item.value }}</el-option>
>{{ item.value }}</el-option
>
</el-select>
</el-radio>
</el-form-item>
@ -42,7 +40,13 @@
<el-input-number v-model="average01" :min="1" :max="4" /> 周的星期
<el-select clearable v-model="average02">
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key">{{ item.value }}</el-option>
<el-option
v-for="item in weekList"
:key="item.key"
:label="item.value"
:value="item.key"
>{{ item.value }}</el-option
>
</el-select>
</el-radio>
</el-form-item>
@ -51,7 +55,13 @@
<el-radio v-model="radioValue" :value="5">
本月最后一个星期
<el-select clearable v-model="weekday">
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key">{{ item.value }}</el-option>
<el-option
v-for="item in weekList"
:key="item.key"
:label="item.value"
:value="item.key"
>{{ item.value }}</el-option
>
</el-select>
</el-radio>
</el-form-item>
@ -59,8 +69,20 @@
<el-form-item>
<el-radio v-model="radioValue" :value="6">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="String(item.key)">{{ item.value }}</el-option>
<el-select
clearable
v-model="checkboxList"
placeholder="可多选"
multiple
style="width: 100%"
>
<el-option
v-for="item in weekList"
:key="item.key"
:label="item.value"
:value="String(item.key)"
>{{ item.value }}</el-option
>
</el-select>
</el-radio>
</el-form-item>
@ -88,7 +110,7 @@ const weekList = [
{ key: 5, value: '星期四' },
{ key: 6, value: '星期五' },
{ key: 7, value: '星期六' },
{ key: 1, value: '星期日' }
{ key: 1, value: '星期日' },
]
const radioValue = ref(2)
@ -179,5 +201,14 @@ function setRadioValue(val: number) {
radioValue.value = val
}
defineExpose({ radioValue, checkboxList, weekday, cycle01, cycle02, average01, average02, setRadioValue })
defineExpose({
radioValue,
checkboxList,
weekday,
cycle01,
cycle02,
average01,
average02,
setRadioValue,
})
</script>

@ -1,15 +1,11 @@
<template>
<el-form size="small">
<el-form-item>
<el-radio :value="1" v-model="radioValue">
不填允许的通配符[, - * /]
</el-radio>
<el-radio :value="1" v-model="radioValue"> [, - * /] </el-radio>
</el-form-item>
<el-form-item>
<el-radio :value="2" v-model="radioValue">
每年
</el-radio>
<el-radio :value="2" v-model="radioValue"> </el-radio>
</el-form-item>
<el-form-item>
@ -24,7 +20,8 @@
<el-radio :value="4" v-model="radioValue">
<el-input-number v-model="average01" :min="fullYear" :max="2098" /> 年开始
<el-input-number v-model="average02" :min="1" :max="2099 - average01 || fullYear" /> 年执行一次
<el-input-number v-model="average02" :min="1" :max="2099 - average01 || fullYear" />
年执行一次
</el-radio>
</el-form-item>
@ -32,7 +29,12 @@
<el-radio :value="5" v-model="radioValue">
指定
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple>
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item - 1 + fullYear" />
<el-option
v-for="item in 9"
:key="item"
:value="item - 1 + fullYear"
:label="item - 1 + fullYear"
/>
</el-select>
</el-radio>
</el-form-item>

@ -16,7 +16,12 @@
<el-icon><Refresh /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="显隐列" effect="dark" placement="top" v-if="columns && columns.length > 0">
<el-tooltip
content="显隐列"
effect="dark"
placement="top"
v-if="columns && columns.length > 0"
>
<el-button circle @click="showColumn">
<el-icon><Menu /></el-icon>
</el-button>
@ -45,7 +50,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
showSearch: true,
columns: () => []
columns: () => [],
})
const emit = defineEmits<{
@ -59,7 +64,7 @@ const open = ref(false)
const transferData = computed(() => {
return (props.columns || []).map((item, index) => ({
key: index,
label: item.label
label: item.label,
}))
})
@ -71,7 +76,7 @@ function refresh() {
emit('queryTable')
}
function dataChange(data: number[]) {
function dataChange(data: (string | number)[]) {
for (const item in props.columns) {
const key = props.columns[item].key
props.columns[item].visible = !data.includes(key)

@ -2,7 +2,11 @@
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span v-if="item.redirect === 'noRedirect' || index === levelList.length - 1" class="no-redirect">{{ item.meta?.title }}</span>
<span
v-if="item.redirect === 'noRedirect' || index === levelList.length - 1"
class="no-redirect"
>{{ item.meta?.title }}</span
>
<a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
</el-breadcrumb-item>
</transition-group>
@ -30,7 +34,7 @@ const levelList = ref<BreadcrumbItem[]>([])
function getBreadcrumb() {
// Only show routes with meta.title
const matched = route.matched.filter(item => item.meta && item.meta.title)
const matched = route.matched.filter((item) => item.meta && item.meta.title)
const first = matched[0]
if (!isDashboard(first)) {
@ -38,7 +42,7 @@ function getBreadcrumb() {
}
levelList.value = matched.filter(
item => item.meta && item.meta.title && item.meta.breadcrumb !== false
(item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
) as unknown as BreadcrumbItem[]
}

@ -29,7 +29,11 @@
</el-tooltip>
</template>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click" @command="handleCommand">
<el-dropdown
class="avatar-container right-menu-item hover-effect"
trigger="click"
@command="handleCommand"
>
<div class="avatar-wrapper">
<img :src="userStore.userInfo?.avatar || defaultAvatar" class="user-avatar" />
<el-icon class="dropdown-arrow"><ArrowDown /></el-icon>
@ -80,7 +84,7 @@ const setting = computed({
},
set(_val: boolean) {
// TODO: integrate with settings store
}
},
})
// Top navigation mode
@ -96,7 +100,7 @@ async function handleCommand(command: string) {
await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
type: 'warning',
})
await userStore.logout()
router.push('/login')

@ -23,13 +23,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import {
HomeFilled,
Setting,
User,
Menu,
Document,
} from '@element-plus/icons-vue'
import { HomeFilled, Setting, User, Menu, Document } from '@element-plus/icons-vue'
const router = useRouter()
const route = useRoute()

@ -1,5 +1,10 @@
<template>
<el-scrollbar ref="scrollContainerRef" :vertical="false" class="scroll-container" @wheel.prevent="handleScroll">
<el-scrollbar
ref="scrollContainerRef"
:vertical="false"
class="scroll-container"
@wheel.prevent="handleScroll"
>
<slot />
</el-scrollbar>
</template>
@ -50,7 +55,7 @@ function moveToTarget(currentTag: HTMLElement) {
wrapper.scrollLeft = wrapper.scrollWidth - containerWidth
} else {
// Find prevTag and nextTag
const currentIndex = tagList.findIndex(item => item === currentTag)
const currentIndex = tagList.findIndex((item) => item === currentTag)
const prevTag = tagList[currentIndex - 1]
const nextTag = tagList[currentIndex + 1]
@ -71,7 +76,7 @@ function moveToTarget(currentTag: HTMLElement) {
}
defineExpose({
moveToTarget
moveToTarget,
})
</script>

@ -16,7 +16,11 @@
>
<span class="tag-link" @click="navigate">
{{ tag.title || tag.meta?.title || tag.path }}
<el-icon v-if="!isAffix(tag)" class="close-icon" @click.prevent.stop="closeSelectedTag(tag)">
<el-icon
v-if="!isAffix(tag)"
class="close-icon"
@click.prevent.stop="closeSelectedTag(tag)"
>
<Close />
</el-icon>
</span>
@ -84,7 +88,7 @@ function activeStyle(tag: TagView): Record<string, string> {
if (!isActive(tag)) return {}
return {
'background-color': theme.value,
'border-color': theme.value
'border-color': theme.value,
}
}
@ -94,7 +98,10 @@ function isAffix(tag: TagView): boolean {
function isFirstView(): boolean {
try {
return selectedTag.value.fullPath === visitedViews.value[1]?.fullPath || selectedTag.value.fullPath === '/index'
return (
selectedTag.value.fullPath === visitedViews.value[1]?.fullPath ||
selectedTag.value.fullPath === '/index'
)
} catch {
return false
}
@ -102,7 +109,9 @@ function isFirstView(): boolean {
function isLastView(): boolean {
try {
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1]?.fullPath
return (
selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1]?.fullPath
)
} catch {
return false
}
@ -111,7 +120,7 @@ function isLastView(): boolean {
function filterAffixTags(routes: any[], basePath = '/'): TagView[] {
const tags: TagView[] = []
routes.forEach(route => {
routes.forEach((route) => {
if (route.meta?.affix) {
const tagPath = path.resolve(basePath, route.path)
tags.push({
@ -119,7 +128,7 @@ function filterAffixTags(routes: any[], basePath = '/'): TagView[] {
path: tagPath,
name: route.name,
title: route.meta?.title,
meta: { ...route.meta }
meta: { ...route.meta },
})
}
if (route.children) {
@ -209,7 +218,7 @@ function closeOthersTags() {
function closeAllTags(view: TagView) {
tagsViewStore.delAllViews().then(({ visitedViews }) => {
if (affixTags.value.some(tag => tag.path === route.path)) {
if (affixTags.value.some((tag) => tag.path === route.path)) {
return
}
toLastView(visitedViews, view)
@ -227,7 +236,9 @@ function toLastView(visitedViewsList: TagView[], _view?: TagView) {
function openMenu(tag: TagView, e: MouseEvent) {
const menuMinWidth = 105
const container = (e.currentTarget as HTMLElement)?.closest('.tags-view-container') as HTMLElement | null
const container = (e.currentTarget as HTMLElement)?.closest(
'.tags-view-container'
) as HTMLElement | null
const offsetLeft = container?.getBoundingClientRect().left || 0
const offsetWidth = container?.offsetWidth || 0
const maxLeft = offsetWidth - menuMinWidth
@ -280,7 +291,9 @@ onMounted(() => {
width: 100%;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
box-shadow:
0 1px 3px 0 rgba(0, 0, 0, 0.12),
0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper {
.tags-view-item {

@ -16,33 +16,31 @@ import {
} from 'echarts/components'
import type { EChartsOption } from 'echarts'
use([
CanvasRenderer,
BarChart,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
])
use([CanvasRenderer, BarChart, TitleComponent, TooltipComponent, GridComponent, LegendComponent])
const props = withDefaults(defineProps<{
title?: string
data: { name: string; value: number }[]
color?: string
height?: string
}>(), {
title: '',
color: '#58a6ff',
height: '300px',
})
const props = withDefaults(
defineProps<{
title?: string
data: { name: string; value: number }[]
color?: string
height?: string
}>(),
{
title: '',
color: '#58a6ff',
height: '300px',
}
)
const option = computed<EChartsOption>(() => ({
title: props.title ? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } } : undefined,
title: props.title
? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } }
: undefined,
tooltip: { trigger: 'axis' },
grid: { left: 40, right: 20, top: props.title ? 30 : 10, bottom: 30 },
xAxis: {
type: 'category',
data: props.data.map(d => d.name),
data: props.data.map((d) => d.name),
axisLine: { lineStyle: { color: '#30363d' } },
axisLabel: { color: '#8b949e', fontSize: 10 },
},
@ -51,12 +49,14 @@ const option = computed<EChartsOption>(() => ({
splitLine: { lineStyle: { color: '#21262d' } },
axisLabel: { color: '#8b949e' },
},
series: [{
type: 'bar',
data: props.data.map(d => d.value),
itemStyle: { color: props.color },
barWidth: '60%',
}],
series: [
{
type: 'bar',
data: props.data.map((d) => d.value),
itemStyle: { color: props.color },
barWidth: '60%',
},
],
}))
</script>

@ -25,22 +25,28 @@ use([
VisualMapComponent,
])
const props = withDefaults(defineProps<{
title?: string
xAxisData: string[]
yAxisData: string[]
data: { x: number; y: number; value: number }[]
height?: string
}>(), {
title: '',
height: '400px',
})
const props = withDefaults(
defineProps<{
title?: string
xAxisData: string[]
yAxisData: string[]
data: { x: number; y: number; value: number }[]
height?: string
}>(),
{
title: '',
height: '400px',
}
)
const option = computed<EChartsOption>(() => ({
title: props.title ? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } } : undefined,
title: props.title
? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } }
: undefined,
tooltip: {
position: 'top',
formatter: (params: any) => `${props.yAxisData[params.data[1]]} / ${props.xAxisData[params.data[0]]}: ${params.data[2]}`,
formatter: (params: any) =>
`${props.yAxisData[params.data[1]]} / ${props.xAxisData[params.data[0]]}: ${params.data[2]}`,
},
grid: { left: 80, right: 80, top: props.title ? 30 : 10, bottom: 40 },
xAxis: {
@ -59,7 +65,7 @@ const option = computed<EChartsOption>(() => ({
},
visualMap: {
min: 0,
max: Math.max(...props.data.map(d => d.value), 1),
max: Math.max(...props.data.map((d) => d.value), 1),
calculable: true,
orient: 'horizontal',
left: 'center',
@ -67,13 +73,15 @@ const option = computed<EChartsOption>(() => ({
inRange: { color: ['#161b22', '#2d9cdb', '#36b37e', '#e3b341', '#ff6b6b', '#ff4444'] },
textStyle: { color: '#8b949e' },
},
series: [{
type: 'heatmap',
data: props.data.map(d => [d.x, d.y, d.value]),
label: { show: true, color: '#e6edf3', fontSize: 10 },
itemStyle: { borderColor: '#30363d', borderWidth: 1 },
emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' } },
}],
series: [
{
type: 'heatmap',
data: props.data.map((d) => [d.x, d.y, d.value]),
label: { show: true, color: '#e6edf3', fontSize: 10 },
itemStyle: { borderColor: '#30363d', borderWidth: 1 },
emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' } },
},
],
}))
</script>

@ -16,31 +16,32 @@ import {
} from 'echarts/components'
import type { EChartsOption } from 'echarts'
use([
CanvasRenderer,
LineChart,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
])
use([CanvasRenderer, LineChart, TitleComponent, TooltipComponent, GridComponent, LegendComponent])
const props = withDefaults(defineProps<{
title?: string
xAxisData: string[]
series: { name: string; data: number[]; color?: string; areaStyle?: boolean }[]
height?: string
inverseY?: boolean
}>(), {
title: '',
height: '300px',
inverseY: false,
})
const props = withDefaults(
defineProps<{
title?: string
xAxisData: string[]
series: { name: string; data: number[]; color?: string; areaStyle?: boolean }[]
height?: string
inverseY?: boolean
}>(),
{
title: '',
height: '300px',
inverseY: false,
}
)
const option = computed<EChartsOption>(() => ({
title: props.title ? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } } : undefined,
title: props.title
? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } }
: undefined,
tooltip: { trigger: 'axis' },
legend: props.series.length > 1 ? { data: props.series.map(s => s.name), textStyle: { color: '#8b949e' } } : undefined,
legend:
props.series.length > 1
? { data: props.series.map((s) => s.name), textStyle: { color: '#8b949e' } }
: undefined,
grid: { left: 40, right: 20, top: props.title ? 30 : 10, bottom: 30 },
xAxis: {
type: 'category',
@ -54,13 +55,15 @@ const option = computed<EChartsOption>(() => ({
splitLine: { lineStyle: { color: '#21262d' } },
axisLabel: { color: '#8b949e' },
},
series: props.series.map(s => ({
series: props.series.map((s) => ({
name: s.name,
type: 'line',
data: s.data,
smooth: true,
itemStyle: { color: s.color || '#58a6ff' },
areaStyle: s.areaStyle ? { color: s.color ? `${s.color}33` : 'rgba(88,166,255,0.2)' } : undefined,
areaStyle: s.areaStyle
? { color: s.color ? `${s.color}33` : 'rgba(88,166,255,0.2)' }
: undefined,
})),
}))
</script>

@ -8,43 +8,40 @@ import VChart from 'vue-echarts'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
LegendComponent,
} from 'echarts/components'
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import type { EChartsOption } from 'echarts'
use([
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent,
])
use([CanvasRenderer, PieChart, TitleComponent, TooltipComponent, LegendComponent])
const props = withDefaults(defineProps<{
title?: string
data: { name: string; value: number }[]
height?: string
}>(), {
title: '',
height: '300px',
})
const props = withDefaults(
defineProps<{
title?: string
data: { name: string; value: number }[]
height?: string
}>(),
{
title: '',
height: '300px',
}
)
const option = computed<EChartsOption>(() => ({
title: props.title ? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } } : undefined,
title: props.title
? { text: props.title, textStyle: { color: '#e6edf3', fontSize: 12 } }
: undefined,
tooltip: { trigger: 'item' },
legend: { orient: 'vertical', right: 10, top: 'center', textStyle: { color: '#8b949e' } },
series: [{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: { borderRadius: 4, borderColor: '#1c2333', borderWidth: 2 },
label: { show: false },
emphasis: { label: { show: true, fontSize: 12, fontWeight: 'bold', color: '#e6edf3' } },
data: props.data,
}],
series: [
{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: { borderRadius: 4, borderColor: '#1c2333', borderWidth: 2 },
label: { show: false },
emphasis: { label: { show: true, fontSize: 12, fontWeight: 'bold', color: '#e6edf3' } },
data: props.data,
},
],
}))
</script>

@ -6,7 +6,8 @@
v-if="item.raw?.listClass === 'default' || item.raw?.listClass === ''"
:index="index"
:class="item.raw?.cssClass"
>{{ item.label }}</span>
>{{ item.label }}</span
>
<el-tag
v-else
:disable-transitions="true"
@ -32,7 +33,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
options: null,
value: undefined
value: undefined,
})
const values = computed(() => {
@ -42,9 +43,17 @@ const values = computed(() => {
return []
})
function getTagType(listClass: string | undefined): string {
if (listClass === 'primary') return ''
return listClass || ''
function getTagType(
listClass: string | undefined
): 'primary' | 'success' | 'info' | 'warning' | 'danger' | undefined {
const typeMap: Record<string, any> = {
primary: 'primary',
success: 'success',
info: 'info',
warning: 'warning',
danger: 'danger',
}
return typeMap[listClass || ''] || undefined
}
</script>

@ -42,7 +42,7 @@ const props = withDefaults(defineProps<Props>(), {
minHeight: undefined,
readOnly: false,
fileSize: 5,
type: 'url'
type: 'url',
})
const emit = defineEmits<{
@ -58,7 +58,7 @@ const editorRef = ref<HTMLElement | null>(null)
const uploadUrl = `${import.meta.env.VITE_APP_BASE_API}/common/upload`
const headers = {
Authorization: `Bearer ${getToken()}`
Authorization: `Bearer ${getToken()}`,
}
let quillInstance: any = null
@ -90,11 +90,11 @@ const options = {
[{ color: [] }, { background: [] }],
[{ align: [] }],
['clean'],
['link', 'image', 'video']
]
['link', 'image', 'video'],
],
},
placeholder: '请输入内容',
readOnly: props.readOnly
readOnly: props.readOnly,
}
function init() {

@ -17,20 +17,34 @@
<template #tip>
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize"> <b class="danger-text">{{ fileSize }}MB</b> </template>
<template v-if="fileType.length"> <b class="danger-text">{{ fileType.join('/') }}</b> </template>
<template v-if="fileSize">
大小不超过 <b class="danger-text">{{ fileSize }}MB</b>
</template>
<template v-if="fileType.length">
格式为 <b class="danger-text">{{ fileType.join('/') }}</b>
</template>
的文件
</div>
</template>
</el-upload>
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="file in fileList">
<transition-group
class="upload-file-list el-upload-list el-upload-list--text"
name="el-fade-in-linear"
tag="ul"
>
<li
:key="file.uid"
class="el-upload-list__item ele-upload-list__item-content"
v-for="file in fileList"
>
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
<el-icon><Document /></el-icon> {{ getFileName(file.name) }}
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(fileList.indexOf(file))" type="danger">删除</el-link>
<el-link :underline="false" @click="handleDelete(fileList.indexOf(file))" type="danger"
>删除</el-link
>
</div>
</li>
</transition-group>
@ -57,7 +71,7 @@ const props = withDefaults(defineProps<Props>(), {
limit: 5,
fileSize: 5,
fileType: () => ['doc', 'xls', 'ppt', 'txt', 'pdf'],
isShowTip: true
isShowTip: true,
})
const emit = defineEmits<{
@ -67,7 +81,7 @@ const emit = defineEmits<{
const baseUrl = import.meta.env.VITE_APP_BASE_API || ''
const uploadFileUrl = `${import.meta.env.VITE_APP_BASE_API}/common/upload`
const headers = {
Authorization: `Bearer ${getToken()}`
Authorization: `Bearer ${getToken()}`,
}
const fileList = ref<UploadFileItem[]>([])
@ -81,7 +95,7 @@ function handleBeforeUpload(file: File): boolean {
if (lastDotIndex > -1) {
fileExtension = file.name.slice(lastDotIndex + 1)
}
const isTypeOk = props.fileType.some(type => {
const isTypeOk = props.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true
if (fileExtension && fileExtension.indexOf(type) > -1) return true
return false
@ -130,7 +144,7 @@ function getFileName(name: string): string {
}
function listToString(list: UploadFileItem[], separator = ','): string {
const urls = list.map(item => item.url)
const urls = list.map((item) => item.url)
return urls.join(separator)
}
@ -140,11 +154,14 @@ watch(
if (val) {
let temp = 1
const list = Array.isArray(val) ? val : String(val).split(',')
fileList.value = list.map(item => {
fileList.value = list.map((item) => {
if (typeof item === 'string') {
return { name: item, url: item, uid: new Date().getTime() + temp++ }
}
return { ...item, uid: (item as UploadFileItem).uid || new Date().getTime() + temp++ } as UploadFileItem
return {
...item,
uid: (item as UploadFileItem).uid || new Date().getTime() + temp++,
} as UploadFileItem
})
} else {
fileList.value = []

@ -88,8 +88,8 @@ function initFuse(list: FuseResultItem[]) {
minMatchCharLength: 1,
keys: [
{ name: 'title', weight: 0.7 },
{ name: 'path', weight: 0.3 }
]
{ name: 'path', weight: 0.3 },
],
})
}
@ -106,7 +106,7 @@ function generateRoutes(
const data: FuseResultItem = {
path: !isHttp(routerItem.path) ? path.resolve(basePath, routerItem.path) : routerItem.path,
title: [...prefixTitle]
title: [...prefixTitle],
}
if (routerItem.meta?.title) {

@ -1,13 +1,19 @@
<template>
<div class="icon-body">
<el-input v-model="name" clearable placeholder="请输入图标名称" @clear="filterIcons" @input="filterIcons">
<el-input
v-model="name"
clearable
placeholder="请输入图标名称"
@clear="filterIcons"
@input="filterIcons"
>
<template #suffix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<div class="icon-list">
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
<svg-icon :icon-class="item" style="height: 30px; width: 16px;" />
<svg-icon :icon-class="item" style="height: 30px; width: 16px" />
<span>{{ item }}</span>
</div>
</div>
@ -29,7 +35,7 @@ const iconList = ref<string[]>([...icons])
function filterIcons() {
iconList.value = [...icons]
if (name.value) {
iconList.value = iconList.value.filter(item => item.includes(name.value))
iconList.value = iconList.value.filter((item) => item.includes(name.value))
}
}

@ -67,7 +67,7 @@ const icons: string[] = [
'arrow-up',
'arrow-down',
'arrow-left',
'arrow-right'
'arrow-right',
]
export default icons

@ -22,8 +22,12 @@
<template #tip>
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize"> <b class="danger-text">{{ fileSize }}MB</b> </template>
<template v-if="fileType.length"> <b class="danger-text">{{ fileType.join('/') }}</b> </template>
<template v-if="fileSize">
大小不超过 <b class="danger-text">{{ fileSize }}MB</b>
</template>
<template v-if="fileType.length">
格式为 <b class="danger-text">{{ fileType.join('/') }}</b>
</template>
的文件
</div>
</template>
@ -55,7 +59,7 @@ const props = withDefaults(defineProps<Props>(), {
limit: 5,
fileSize: 5,
fileType: () => ['png', 'jpg', 'jpeg'],
isShowTip: true
isShowTip: true,
})
const emit = defineEmits<{
@ -67,7 +71,7 @@ const dialogVisible = ref(false)
const baseUrl = import.meta.env.VITE_APP_BASE_API || ''
const uploadImgUrl = `${import.meta.env.VITE_APP_BASE_API}/common/upload`
const headers = {
Authorization: `Bearer ${getToken()}`
Authorization: `Bearer ${getToken()}`,
}
const fileList = ref<UploadFileItem[]>([])
let loading: ReturnType<typeof ElLoading.service> | null = null
@ -75,7 +79,7 @@ let loading: ReturnType<typeof ElLoading.service> | null = null
const showTip = computed(() => props.isShowTip && (props.fileType.length > 0 || props.fileSize > 0))
function handleRemove(file: UploadFile) {
const findex = fileList.value.findIndex(f => f.name === file.name)
const findex = fileList.value.findIndex((f) => f.name === file.name)
if (findex > -1) {
fileList.value.splice(findex, 1)
emit('update:modelValue', listToString(fileList.value))
@ -99,7 +103,7 @@ function handleBeforeUpload(file: File): boolean {
if (lastDotIndex > -1) {
fileExtension = file.name.slice(lastDotIndex + 1)
}
isImg = props.fileType.some(type => {
isImg = props.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true
if (fileExtension && fileExtension.indexOf(type) > -1) return true
return false
@ -124,7 +128,7 @@ function handleBeforeUpload(file: File): boolean {
loading = ElLoading.service({
lock: true,
text: '上传中',
background: 'rgba(0, 0, 0, 0.7)'
background: 'rgba(0, 0, 0, 0.7)',
})
return true
@ -148,7 +152,7 @@ function handlePictureCardPreview(file: UploadFile) {
}
function listToString(list: UploadFileItem[], separator = ','): string {
const urls = list.map(item => item.url.replace(baseUrl, ''))
const urls = list.map((item) => item.url.replace(baseUrl, ''))
return urls.join(separator)
}
@ -157,7 +161,7 @@ watch(
(val) => {
if (val) {
const list = Array.isArray(val) ? val : String(val).split(',')
fileList.value = list.map(item => {
fileList.value = list.map((item) => {
if (typeof item === 'string') {
const fullUrl = item.indexOf(baseUrl) === -1 ? baseUrl + item : item
return { name: fullUrl, url: fullUrl }

@ -40,7 +40,7 @@ const props = withDefaults(defineProps<Props>(), {
layout: 'total, sizes, prev, pager, next, jumper',
background: true,
autoScroll: true,
hidden: false
hidden: false,
})
const emit = defineEmits<{
@ -55,7 +55,7 @@ const currentPage = computed({
},
set(val: number) {
emit('update:page', val)
}
},
})
const pageSize = computed({
@ -64,7 +64,7 @@ const pageSize = computed({
},
set(val: number) {
emit('update:limit', val)
}
},
})
function handleSizeChange(val: number) {

@ -34,7 +34,7 @@ const tagsViewStore = useTagsViewStore()
const sizeOptions = ref([
{ label: 'Default', value: 'default' as const },
{ label: 'Large', value: 'large' as const },
{ label: 'Small', value: 'small' as const }
{ label: 'Small', value: 'small' as const },
])
function handleSetSize(size: 'default' | 'large' | 'small') {

@ -14,7 +14,10 @@
router
>
<template v-for="route in permissionStore.sidebarRoutes" :key="route.path">
<el-menu-item v-if="!route.children || route.children.length === 0" :index="resolvePath(route.path)">
<el-menu-item
v-if="!route.children || route.children.length === 0"
:index="resolvePath(route.path)"
>
<el-icon v-if="route.meta?.icon">
<component :is="route.meta.icon" />
</el-icon>
@ -99,7 +102,7 @@ const appStore = useAppStore()
const activeMenu = computed(() => route.path)
const breadcrumbs = computed(() => {
return route.matched.filter(item => item.meta?.title)
return route.matched.filter((item) => item.meta?.title)
})
const cachedViews = computed(() => permissionStore.cachedViews)

@ -31,7 +31,7 @@ export const dynamicRoutes: RouteRecordRaw[] = []
export function filterAsyncRoutes(routes: RouteRecordRaw[], roles: string[]): RouteRecordRaw[] {
const result: RouteRecordRaw[] = []
routes.forEach(route => {
routes.forEach((route) => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
@ -48,5 +48,5 @@ function hasPermission(roles: string[], route: RouteRecordRaw): boolean {
if (!route.meta?.roles) {
return true
}
return roles.some(role => (route.meta?.roles as string[])?.includes(role))
return roles.some((role) => (route.meta?.roles as string[])?.includes(role))
}

@ -35,7 +35,7 @@ router.beforeEach(async (to, _from, next) => {
const accessRoutes = await permissionStore.generateRoutes()
// Add dynamic routes to router
accessRoutes.forEach(route => {
accessRoutes.forEach((route) => {
if (!router.hasRoute(route.name || route.path)) {
router.addRoute(route)
}

@ -100,8 +100,8 @@ export const usePermissionStore = defineStore('permission', () => {
*/
function filterHiddenRoutes(routeList: RouteRecordRaw[]): RouteRecordRaw[] {
return routeList
.filter(r => !r.meta?.hidden)
.map(r => ({
.filter((r) => !r.meta?.hidden)
.map((r) => ({
...r,
children: r.children ? filterHiddenRoutes(r.children) : undefined,
})) as RouteRecordRaw[]
@ -114,7 +114,7 @@ export const usePermissionStore = defineStore('permission', () => {
return path
.split('/')
.filter(Boolean)
.map(segment => segment.charAt(0).toUpperCase() + segment.slice(1))
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
.join('')
}

@ -7,7 +7,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
const cachedViews = ref<string[]>([])
function addVisitedView(view: TagView) {
const index = visitedViews.value.findIndex(v => v.path === view.path)
const index = visitedViews.value.findIndex((v) => v.path === view.path)
if (index >= 0) {
visitedViews.value[index] = { ...visitedViews.value[index], ...view }
} else {
@ -33,8 +33,8 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
function delVisitedView(view: TagView): Promise<TagView[]> {
return new Promise(resolve => {
const index = visitedViews.value.findIndex(v => v.path === view.path)
return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v) => v.path === view.path)
if (index > -1) {
visitedViews.value.splice(index, 1)
}
@ -43,7 +43,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
function delCachedView(view: TagView): Promise<string[]> {
return new Promise(resolve => {
return new Promise((resolve) => {
const index = cachedViews.value.indexOf(view.name as string)
if (index > -1) {
cachedViews.value.splice(index, 1)
@ -53,19 +53,19 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
function delView(view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> {
return new Promise(async resolve => {
return new Promise(async (resolve) => {
await delVisitedView(view)
await delCachedView(view)
resolve({
visitedViews: [...visitedViews.value],
cachedViews: [...cachedViews.value]
cachedViews: [...cachedViews.value],
})
})
}
function delVisitedViewsFrom(view: TagView): Promise<TagView[]> {
return new Promise(resolve => {
const index = visitedViews.value.findIndex(v => v.path === view.path)
return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v) => v.path === view.path)
if (index > -1) {
visitedViews.value.splice(index, visitedViews.value.length - index)
}
@ -74,7 +74,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
function delCachedViewsFrom(view: TagView): Promise<string[]> {
return new Promise(resolve => {
return new Promise((resolve) => {
const index = cachedViews.value.indexOf(view.name as string)
if (index > -1) {
cachedViews.value.splice(index, cachedViews.value.length - index)
@ -83,20 +83,22 @@ export const useTagsViewStore = defineStore('tagsView', () => {
})
}
function delViewsFrom(view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> {
return new Promise(async resolve => {
function delViewsFrom(
view: TagView
): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> {
return new Promise(async (resolve) => {
await delVisitedViewsFrom(view)
await delCachedViewsFrom(view)
resolve({
visitedViews: [...visitedViews.value],
cachedViews: [...cachedViews.value]
cachedViews: [...cachedViews.value],
})
})
}
function delVisitedViewsTo(view: TagView): Promise<TagView[]> {
return new Promise(resolve => {
const index = visitedViews.value.findIndex(v => v.path === view.path)
return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v) => v.path === view.path)
if (index > -1) {
for (let i = index; i >= 0; i--) {
if (visitedViews.value[i].meta?.affix) continue
@ -108,7 +110,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
function delCachedViewsTo(view: TagView): Promise<string[]> {
return new Promise(resolve => {
return new Promise((resolve) => {
const index = cachedViews.value.indexOf(view.name as string)
if (index > -1) {
cachedViews.value.splice(0, index + 1)
@ -118,31 +120,31 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}
function delViewsTo(view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> {
return new Promise(async resolve => {
return new Promise(async (resolve) => {
await delVisitedViewsTo(view)
await delCachedViewsTo(view)
resolve({
visitedViews: [...visitedViews.value],
cachedViews: [...cachedViews.value]
cachedViews: [...cachedViews.value],
})
})
}
function delAllViews(): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> {
return new Promise(resolve => {
return new Promise((resolve) => {
// Keep affix tags
const affixTags = visitedViews.value.filter(tag => tag.meta?.affix)
const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix)
visitedViews.value = affixTags
cachedViews.value = []
resolve({
visitedViews: [...visitedViews.value],
cachedViews: [...cachedViews.value]
cachedViews: [...cachedViews.value],
})
})
}
function updateVisitedView(view: TagView) {
const index = visitedViews.value.findIndex(v => v.path === view.path)
const index = visitedViews.value.findIndex((v) => v.path === view.path)
if (index >= 0) {
visitedViews.value[index] = { ...visitedViews.value[index], ...view }
}
@ -171,6 +173,6 @@ export const useTagsViewStore = defineStore('tagsView', () => {
delViewsTo,
delAllViews,
updateVisitedView,
resetTagsView
resetTagsView,
}
})

@ -8,8 +8,7 @@
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
@ -41,8 +40,7 @@
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
--shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
@ -142,8 +140,7 @@ code {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) scale(1.4);
}
.vite {
@ -151,8 +148,7 @@ code {
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) scale(0.8);
}
}

@ -1,24 +1,24 @@
// demo-dashboard-heatmap.html
:root {
//
--bg0: #0d1117; //
--bg1: #161b22; //
--bg2: #1c2333; //
--bg3: #2a3040; // hover
--bg0: #0d1117; //
--bg1: #161b22; //
--bg2: #1c2333; //
--bg3: #2a3040; // hover
//
--bd: #30363d;
//
--t1: #e6edf3; //
--t2: #8b949e; //
--tm: #6e7681; //
--t1: #e6edf3; //
--t2: #8b949e; //
--tm: #6e7681; //
//
--red: #f85149; //
--grn: #3fb950; // 绿
--org: #d29922; //
--blu: #58a6ff; //
--red: #f85149; //
--grn: #3fb950; // 绿
--org: #d29922; //
--blu: #58a6ff; //
//
--color-success: var(--grn);
@ -55,7 +55,8 @@
box-sizing: border-box;
}
html, body {
html,
body {
margin: 0;
padding: 0;
background-color: var(--bg0);

@ -100,7 +100,7 @@ export interface DictDataOption {
export interface UploadFileItem {
name: string
url: string
uid?: number | string
uid?: number
}
/**

@ -34,7 +34,7 @@ export function checkPermi(permissions: string[]): boolean {
if (!permissions || permissions.length === 0) {
return false
}
return permissions.some(p => hasPermission(p))
return permissions.some((p) => hasPermission(p))
}
/**
@ -44,5 +44,5 @@ export function checkRole(roles: string[]): boolean {
if (!roles || roles.length === 0) {
return false
}
return roles.some(r => hasRole(r))
return roles.some((r) => hasRole(r))
}

@ -10,12 +10,12 @@ function easeInOutQuad(t: number, b: number, c: number, d: number): number {
const requestAnimFrame =
typeof window !== 'undefined'
? (window.requestAnimationFrame ||
? window.requestAnimationFrame ||
(window as any).webkitRequestAnimationFrame ||
(window as any).mozRequestAnimationFrame ||
function (callback: FrameRequestCallback): number {
return window.setTimeout(callback, 1000 / 60) as unknown as number
})
}
: function (callback: FrameRequestCallback): number {
return setTimeout(callback, 1000 / 60) as unknown as number
}
@ -23,7 +23,12 @@ const requestAnimFrame =
/**
* Move the scrollbar to a specific position
*/
function move(amountToScroll: number, start: number, increment: number, startTime: number = Date.now()): void {
function move(
amountToScroll: number,
start: number,
increment: number,
startTime: number = Date.now()
): void {
if (typeof window === 'undefined') return
const body = window.document.body
@ -51,7 +56,11 @@ export function scrollTo(element: number | Element, _duration: number = 500): vo
if (typeof window === 'undefined') return
if (typeof element === 'number') {
const start = window.document.documentElement.scrollTop || window.pageYOffset || window.document.body.scrollTop || 0
const start =
window.document.documentElement.scrollTop ||
window.pageYOffset ||
window.document.body.scrollTop ||
0
const amountToScroll = element - start
move(amountToScroll, start, element)
} else {

@ -1,8 +1,21 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="交易日期" prop="tradeDay">
<el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" />
<el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
@ -11,10 +24,48 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['booksystem:account:add']"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['booksystem:account:edit']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['booksystem:account:remove']"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['booksystem:account:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['booksystem:account:add']"
>新增</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['booksystem:account:edit']"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['booksystem:account:remove']"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['booksystem:account:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -29,18 +80,45 @@
<el-table-column label="当日总资产盈亏比例" align="center" prop="totalDiff" />
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="交易日期" prop="tradeDay">
<el-date-picker clearable size="small" v-model="form.tradeDay" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" @change="handleChange" />
<el-date-picker
clearable
size="small"
v-model="form.tradeDay"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
@change="handleChange"
/>
</el-form-item>
<el-form-item label="净资产" prop="assets"><el-input v-model="form.assets" placeholder="请输入净资产" /></el-form-item>
<el-form-item label="总资产" prop="totalAssets"><el-input v-model="form.totalAssets" placeholder="请输入总资产" /></el-form-item>
<el-form-item label="当日盈亏" prop="profit"><el-input v-model="form.profit" placeholder="请输入当日盈亏" /></el-form-item>
<el-form-item label="净资产盈亏比例" prop="assetsDiff"><el-input v-model="form.assetsDiff" placeholder="请输入净资产盈亏比例" /></el-form-item>
<el-form-item label="总资产盈亏比例" prop="totalDiff"><el-input v-model="form.totalDiff" placeholder="请输入总资产盈亏比例" /></el-form-item>
<el-form-item label="净资产" prop="assets"
><el-input v-model="form.assets" placeholder="请输入净资产"
/></el-form-item>
<el-form-item label="总资产" prop="totalAssets"
><el-input v-model="form.totalAssets" placeholder="请输入总资产"
/></el-form-item>
<el-form-item label="当日盈亏" prop="profit"
><el-input v-model="form.profit" placeholder="请输入当日盈亏"
/></el-form-item>
<el-form-item label="净资产盈亏比例" prop="assetsDiff"
><el-input v-model="form.assetsDiff" placeholder="请输入净资产盈亏比例"
/></el-form-item>
<el-form-item label="总资产盈亏比例" prop="totalDiff"
><el-input v-model="form.totalDiff" placeholder="请输入总资产盈亏比例"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -54,7 +132,13 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getAccountList, getAccount, delAccount, addAccount, updateAccount } from '@/api/modules/booksystem/account'
import {
getAccountList,
getAccount,
delAccount,
addAccount,
updateAccount,
} from '@/api/modules/booksystem/account'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -70,26 +154,119 @@ const open = ref(false)
const weekOptions = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
const queryParams = reactive({ pageNum: 1, pageSize: 10, tradeDay: '' })
const form = ref<any>({ id: null, tradeDay: '', weekDay: '', assets: '', totalAssets: '', profit: '', assetsDiff: '', totalDiff: '' })
const form = ref<any>({
id: null,
tradeDay: '',
weekDay: '',
assets: '',
totalAssets: '',
profit: '',
assetsDiff: '',
totalDiff: '',
})
const rules = {}
async function getList() { loading.value = true; try { const res: any = await getAccountList(queryParams); accountList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { id: null, tradeDay: '', weekDay: '', assets: '', totalAssets: '', profit: '', assetsDiff: '', totalDiff: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加交易账户' }
async function handleUpdate(row?: any) { reset(); const id = row?.id || ids.value[0]; const res = await getAccount(id); form.value = res.data; open.value = true; title.value = '修改交易账户' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.id != null) { await updateAccount(form.value); ElMessage.success('修改成功') } else { await addAccount(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const idList = row?.id || ids.value; ElMessageBox.confirm(`是否确认删除交易账户编号为"${idList}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delAccount(idList); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/account/export`, '_blank') }
function handleChange(value: any) { if (!value) return; const currentDate = new Date(value as string); const getWeek = currentDate.getDay(); form.value.weekDay = weekOptions[getWeek] }
async function getList() {
loading.value = true
try {
const res: any = await getAccountList(queryParams)
accountList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
id: null,
tradeDay: '',
weekDay: '',
assets: '',
totalAssets: '',
profit: '',
assetsDiff: '',
totalDiff: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加交易账户'
}
async function handleUpdate(row?: any) {
reset()
const id = row?.id || ids.value[0]
const res = await getAccount(id)
form.value = res.data
open.value = true
title.value = '修改交易账户'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.id != null) {
await updateAccount(form.value)
ElMessage.success('修改成功')
} else {
await addAccount(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const idList = row?.id || ids.value
ElMessageBox.confirm(`是否确认删除交易账户编号为"${idList}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delAccount(idList)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/account/export`, '_blank')
}
function handleChange(value: any) {
if (!value) return
const currentDate = new Date(value as string)
const getWeek = currentDate.getDay()
form.value.weekDay = weekOptions[getWeek]
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,8 +1,21 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="交易日期" prop="tradeDay">
<el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" />
<el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/>
</el-form-item>
<el-form-item label="股票代码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入股票代码" clearable size="small" />
@ -17,10 +30,22 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport"></el-button></el-col>
<el-col :span="1.5"
><el-button type="primary" plain size="small" @click="handleAdd"></el-button></el-col
>
<el-col :span="1.5"
><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button type="warning" plain size="small" @click="handleExport"></el-button></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -38,33 +63,96 @@
<el-table-column label="操作逻辑" align="center" prop="dealLogic" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['booksystem:book:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['booksystem:book:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['booksystem:book:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['booksystem:book:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="70%" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto" label-position="left">
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="form.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="股票代码" prop="code"><el-input v-model="form.code" placeholder="请输入股票代码" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="操作" prop="operate"><el-input v-model="form.operate" placeholder="请输入操作" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="form.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="股票代码" prop="code"
><el-input v-model="form.code" placeholder="请输入股票代码" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="操作" prop="operate"
><el-input v-model="form.operate" placeholder="请输入操作" /></el-form-item
></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="成交量" prop="volumn"><el-input v-model="form.volumn" placeholder="请输入成交量" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="成交价格" prop="dealPrice"><el-input v-model="form.dealPrice" placeholder="请输入成交价格" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="成交额" prop="amount"><el-input v-model="form.amount" placeholder="请输入成交额" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="成交量" prop="volumn"
><el-input v-model="form.volumn" placeholder="请输入成交量" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="成交价格" prop="dealPrice"
><el-input v-model="form.dealPrice" placeholder="请输入成交价格" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="成交额" prop="amount"
><el-input v-model="form.amount" placeholder="请输入成交额" /></el-form-item
></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="印花税" prop="tax"><el-input v-model="form.tax" placeholder="请输入印花税" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="手续费" prop="fee"><el-input v-model="form.fee" placeholder="请输入手续费" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="其他费用" prop="other"><el-input v-model="form.other" placeholder="请输入其他费用" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="印花税" prop="tax"
><el-input v-model="form.tax" placeholder="请输入印花税" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="手续费" prop="fee"
><el-input v-model="form.fee" placeholder="请输入手续费" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="其他费用" prop="other"
><el-input v-model="form.other" placeholder="请输入其他费用" /></el-form-item
></el-col>
</el-row>
<el-row>
<el-col :span="24"><el-form-item label="交易逻辑" prop="dealLogic"><el-input v-model="form.dealLogic" type="textarea" autosize placeholder="请输入交易逻辑" /></el-form-item></el-col>
<el-col :span="24"
><el-form-item label="交易逻辑" prop="dealLogic"
><el-input
v-model="form.dealLogic"
type="textarea"
autosize
placeholder="请输入交易逻辑" /></el-form-item
></el-col>
</el-row>
</el-form>
<template #footer>
@ -94,28 +182,124 @@ const title = ref('')
const open = ref(false)
const queryParams = reactive({ pageNum: 1, pageSize: 10, code: '', name: '', tradeDay: '' })
const form = ref<any>({ id: null, code: '', name: '', tradeDay: '', operate: '', dealPrice: '', volumn: '', amount: '', tax: '', fee: '', other: '', dealLogic: '' })
const form = ref<any>({
id: null,
code: '',
name: '',
tradeDay: '',
operate: '',
dealPrice: '',
volumn: '',
amount: '',
tax: '',
fee: '',
other: '',
dealLogic: '',
})
const rules = {
code: [{ required: true, message: '股票代码不能为空', trigger: 'blur' }],
operate: [{ required: true, message: '操作不能为空', trigger: 'blur' }]
operate: [{ required: true, message: '操作不能为空', trigger: 'blur' }],
}
async function getList() { loading.value = true; try { const res: any = await getBookList(queryParams); bookList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { id: null, code: '', name: '', tradeDay: '', operate: '', dealPrice: '', volumn: '', amount: '', tax: '', fee: '', other: '', dealLogic: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加交易记账' }
async function handleUpdate(row?: any) { reset(); const id = row?.id || ids.value[0]; const res = await getBook(id); form.value = res.data; open.value = true; title.value = '修改交易记账' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.id != null) { await updateBook(form.value); ElMessage.success('修改成功') } else { await addBook(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const idList = row?.id || ids.value; ElMessageBox.confirm(`是否确认删除交易记账编号为"${idList}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delBook(idList); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/book/export`, '_blank') }
async function getList() {
loading.value = true
try {
const res: any = await getBookList(queryParams)
bookList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
id: null,
code: '',
name: '',
tradeDay: '',
operate: '',
dealPrice: '',
volumn: '',
amount: '',
tax: '',
fee: '',
other: '',
dealLogic: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加交易记账'
}
async function handleUpdate(row?: any) {
reset()
const id = row?.id || ids.value[0]
const res = await getBook(id)
form.value = res.data
open.value = true
title.value = '修改交易记账'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.id != null) {
await updateBook(form.value)
ElMessage.success('修改成功')
} else {
await addBook(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const idList = row?.id || ids.value
ElMessageBox.confirm(`是否确认删除交易记账编号为"${idList}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delBook(idList)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/book/export`, '_blank')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,9 +1,37 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="股票代码" prop="code"><el-input v-model="queryParams.code" placeholder="请输入股票代码" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="股票名称" prop="name"><el-input v-model="queryParams.name" placeholder="请输入股票名称" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="股票代码" prop="code"
><el-input
v-model="queryParams.code"
placeholder="请输入股票代码"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="股票名称" prop="name"
><el-input
v-model="queryParams.name"
placeholder="请输入股票名称"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/></el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
<el-button size="small" @click="resetQuery"></el-button>
@ -11,10 +39,48 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['booksystem:operations:add']"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['booksystem:operations:edit']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['booksystem:operations:remove']"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['booksystem:operations:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['booksystem:operations:add']"
>新增</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['booksystem:operations:edit']"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['booksystem:operations:remove']"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['booksystem:operations:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -37,36 +103,100 @@
<el-table-column label="备注" align="center" prop="bz" />
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="70%" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="股票代码" prop="code"><el-input v-model="form.code" placeholder="请输入股票代码" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="股票名称" prop="name"><el-input v-model="form.name" placeholder="请输入股票名称" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="form.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" @change="handleChange" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="股票代码" prop="code"
><el-input v-model="form.code" placeholder="请输入股票代码" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="股票名称" prop="name"
><el-input v-model="form.name" placeholder="请输入股票名称" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="form.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
@change="handleChange" /></el-form-item
></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="操作" prop="operate">
<el-select v-model="form.operate" placeholder="请选择操作">
<el-option label="买入" value="买入" /><el-option label="卖出" value="卖出" />
</el-select>
</el-form-item></el-col>
<el-col :span="8"><el-form-item label="交易价格" prop="dealPrice"><el-input v-model="form.dealPrice" placeholder="请输入交易价格" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="成交量" prop="volumn"><el-input v-model="form.volumn" placeholder="请输入成交量" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="操作" prop="operate">
<el-select v-model="form.operate" placeholder="请选择操作">
<el-option label="买入" value="买入" /><el-option label="卖出" value="卖出" />
</el-select> </el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="交易价格" prop="dealPrice"
><el-input v-model="form.dealPrice" placeholder="请输入交易价格" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="成交量" prop="volumn"
><el-input v-model="form.volumn" placeholder="请输入成交量" /></el-form-item
></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="成交额" prop="amount"><el-input v-model="form.amount" placeholder="请输入成交额" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="印花税" prop="tax"><el-input v-model="form.tax" placeholder="请输入印花税" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="手续费" prop="fee"><el-input v-model="form.fee" placeholder="请输入手续费" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="成交额" prop="amount"
><el-input v-model="form.amount" placeholder="请输入成交额" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="印花税" prop="tax"
><el-input v-model="form.tax" placeholder="请输入印花税" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="手续费" prop="fee"
><el-input v-model="form.fee" placeholder="请输入手续费" /></el-form-item
></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="其他费用" prop="other"><el-input v-model="form.other" placeholder="请输入其他费用" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="操作时涨跌" prop="operateDiff"><el-input v-model="form.operateDiff" placeholder="请输入操作时涨跌" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="关联操作id" prop="preId"><el-input v-model="form.preId" placeholder="请输入关联操作id" /></el-form-item></el-col>
<el-col :span="8"
><el-form-item label="其他费用" prop="other"
><el-input v-model="form.other" placeholder="请输入其他费用" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="操作时涨跌" prop="operateDiff"
><el-input v-model="form.operateDiff" placeholder="请输入操作时涨跌" /></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="关联操作id" prop="preId"
><el-input v-model="form.preId" placeholder="请输入关联操作id" /></el-form-item
></el-col>
</el-row>
<el-row><el-col :span="24"><el-form-item label="操作逻辑" prop="dealLogic"><el-input v-model="form.dealLogic" type="textarea" placeholder="请输入内容" /></el-form-item></el-col></el-row>
<el-row><el-col :span="24"><el-form-item label="备注" prop="bz"><el-input v-model="form.bz" type="textarea" placeholder="请输入内容" /></el-form-item></el-col></el-row>
<el-row
><el-col :span="24"
><el-form-item label="操作逻辑" prop="dealLogic"
><el-input
v-model="form.dealLogic"
type="textarea"
placeholder="请输入内容" /></el-form-item></el-col
></el-row>
<el-row
><el-col :span="24"
><el-form-item label="备注" prop="bz"
><el-input
v-model="form.bz"
type="textarea"
placeholder="请输入内容" /></el-form-item></el-col
></el-row>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -80,7 +210,13 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getOperationsList, getOperations, delOperations, addOperations, updateOperations } from '@/api/modules/booksystem/operations'
import {
getOperationsList,
getOperations,
delOperations,
addOperations,
updateOperations,
} from '@/api/modules/booksystem/operations'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -96,29 +232,137 @@ const open = ref(false)
const weekOptions = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
const queryParams = reactive({ pageNum: 1, pageSize: 10, code: '', name: '', tradeDay: '' })
const form = ref<any>({ id: null, code: '', name: '', tradeDay: '', weekDay: '', operate: '', dealPrice: '', volumn: '', amount: '', tax: '', fee: '', other: '', operateDiff: '', preId: '', dealLogic: '', bz: '' })
const form = ref<any>({
id: null,
code: '',
name: '',
tradeDay: '',
weekDay: '',
operate: '',
dealPrice: '',
volumn: '',
amount: '',
tax: '',
fee: '',
other: '',
operateDiff: '',
preId: '',
dealLogic: '',
bz: '',
})
const rules = {
code: [{ required: true, message: '股票代码不能为空', trigger: 'blur' }],
operate: [{ required: true, message: '操作不能为空', trigger: 'blur' }]
operate: [{ required: true, message: '操作不能为空', trigger: 'blur' }],
}
async function getList() { loading.value = true; try { const res: any = await getOperationsList(queryParams); operationsList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { id: null, code: '', name: '', tradeDay: '', weekDay: '', operate: '', dealPrice: '', volumn: '', amount: '', tax: '', fee: '', other: '', operateDiff: '', preId: '', dealLogic: '', bz: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加当日操作' }
async function handleUpdate(row?: any) { reset(); const id = row?.id || ids.value[0]; const res = await getOperations(id); form.value = res.data; open.value = true; title.value = '修改当日操作' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.id != null) { await updateOperations(form.value); ElMessage.success('修改成功') } else { await addOperations(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const idList = row?.id || ids.value; ElMessageBox.confirm(`是否确认删除操作编号为"${idList}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delOperations(idList); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/operations/export`, '_blank') }
function handleChange(value: any) { if (!value) return; const currentDate = new Date(value as string); form.value.weekDay = weekOptions[currentDate.getDay()] }
async function getList() {
loading.value = true
try {
const res: any = await getOperationsList(queryParams)
operationsList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
id: null,
code: '',
name: '',
tradeDay: '',
weekDay: '',
operate: '',
dealPrice: '',
volumn: '',
amount: '',
tax: '',
fee: '',
other: '',
operateDiff: '',
preId: '',
dealLogic: '',
bz: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加当日操作'
}
async function handleUpdate(row?: any) {
reset()
const id = row?.id || ids.value[0]
const res = await getOperations(id)
form.value = res.data
open.value = true
title.value = '修改当日操作'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.id != null) {
await updateOperations(form.value)
ElMessage.success('修改成功')
} else {
await addOperations(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const idList = row?.id || ids.value
ElMessageBox.confirm(`是否确认删除操作编号为"${idList}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delOperations(idList)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/operations/export`, '_blank')
}
function handleChange(value: any) {
if (!value) return
const currentDate = new Date(value as string)
form.value.weekDay = weekOptions[currentDate.getDay()]
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,9 +1,37 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="股票代码" prop="code"><el-input v-model="queryParams.code" placeholder="请输入股票代码" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="股票名称" prop="name"><el-input v-model="queryParams.name" placeholder="请输入股票名称" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="股票代码" prop="code"
><el-input
v-model="queryParams.code"
placeholder="请输入股票代码"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="股票名称" prop="name"
><el-input
v-model="queryParams.name"
placeholder="请输入股票名称"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/></el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
<el-button size="small" @click="resetQuery"></el-button>
@ -11,10 +39,48 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['booksystem:statistics:add']"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['booksystem:statistics:edit']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['booksystem:statistics:remove']"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['booksystem:statistics:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['booksystem:statistics:add']"
>新增</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['booksystem:statistics:edit']"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['booksystem:statistics:remove']"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['booksystem:statistics:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -30,17 +96,46 @@
<el-table-column label="备注" align="center" prop="bz" />
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="股票代码" prop="code"><el-input v-model="form.code" placeholder="请输入股票代码" /></el-form-item>
<el-form-item label="股票名称" prop="name"><el-input v-model="form.name" placeholder="请输入股票名称" /></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="form.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" /></el-form-item>
<el-form-item label="操作id" prop="operationsId"><el-input v-model="form.operationsId" placeholder="请输入操作id" /></el-form-item>
<el-form-item label="当笔盈亏" prop="profit"><el-input v-model="form.profit" placeholder="请输入当笔当日盈亏" /></el-form-item>
<el-form-item label="盈亏比例" prop="diff"><el-input v-model="form.diff" placeholder="请输入盈亏比例" /></el-form-item>
<el-form-item label="备注" prop="bz"><el-input v-model="form.bz" type="textarea" placeholder="请输入内容" /></el-form-item>
<el-form-item label="股票代码" prop="code"
><el-input v-model="form.code" placeholder="请输入股票代码"
/></el-form-item>
<el-form-item label="股票名称" prop="name"
><el-input v-model="form.name" placeholder="请输入股票名称"
/></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="form.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/></el-form-item>
<el-form-item label="操作id" prop="operationsId"
><el-input v-model="form.operationsId" placeholder="请输入操作id"
/></el-form-item>
<el-form-item label="当笔盈亏" prop="profit"
><el-input v-model="form.profit" placeholder="请输入当笔当日盈亏"
/></el-form-item>
<el-form-item label="盈亏比例" prop="diff"
><el-input v-model="form.diff" placeholder="请输入盈亏比例"
/></el-form-item>
<el-form-item label="备注" prop="bz"
><el-input v-model="form.bz" type="textarea" placeholder="请输入内容"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -54,7 +149,13 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getStatisticsList, getStatistics, delStatistics, addStatistics, updateStatistics } from '@/api/modules/booksystem/statistics'
import {
getStatisticsList,
getStatistics,
delStatistics,
addStatistics,
updateStatistics,
} from '@/api/modules/booksystem/statistics'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -69,28 +170,118 @@ const title = ref('')
const open = ref(false)
const queryParams = reactive({ pageNum: 1, pageSize: 10, code: '', name: '', tradeDay: '' })
const form = ref<any>({ id: null, code: '', name: '', tradeDay: '', weekDay: '', operationsId: '', profit: '', diff: '', bz: '' })
const form = ref<any>({
id: null,
code: '',
name: '',
tradeDay: '',
weekDay: '',
operationsId: '',
profit: '',
diff: '',
bz: '',
})
const rules = {
code: [{ required: true, message: '股票代码不能为空', trigger: 'blur' }],
operationsId: [{ required: true, message: '操作id不能为空', trigger: 'blur' }]
operationsId: [{ required: true, message: '操作id不能为空', trigger: 'blur' }],
}
async function getList() { loading.value = true; try { const res: any = await getStatisticsList(queryParams); statisticsList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { id: null, code: '', name: '', tradeDay: '', weekDay: '', operationsId: '', profit: '', diff: '', bz: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加单笔操作统计' }
async function handleUpdate(row?: any) { reset(); const id = row?.id || ids.value[0]; const res = await getStatistics(id); form.value = res.data; open.value = true; title.value = '修改单笔操作统计' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.id != null) { await updateStatistics(form.value); ElMessage.success('修改成功') } else { await addStatistics(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const idList = row?.id || ids.value; ElMessageBox.confirm(`是否确认删除统计编号为"${idList}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delStatistics(idList); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/statistics/export`, '_blank') }
async function getList() {
loading.value = true
try {
const res: any = await getStatisticsList(queryParams)
statisticsList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
id: null,
code: '',
name: '',
tradeDay: '',
weekDay: '',
operationsId: '',
profit: '',
diff: '',
bz: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加单笔操作统计'
}
async function handleUpdate(row?: any) {
reset()
const id = row?.id || ids.value[0]
const res = await getStatistics(id)
form.value = res.data
open.value = true
title.value = '修改单笔操作统计'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.id != null) {
await updateStatistics(form.value)
ElMessage.success('修改成功')
} else {
await addStatistics(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const idList = row?.id || ids.value
ElMessageBox.confirm(`是否确认删除统计编号为"${idList}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delStatistics(idList)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/booksystem/statistics/export`, '_blank')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -11,7 +11,9 @@
:key="m"
:class="['mtab', { on: selectedMarket === m }]"
@click="selectedMarket = m"
>{{ m }}</div>
>
{{ m }}
</div>
</div>
<!-- Mini stats -->
@ -75,7 +77,10 @@
<!-- Distribution chart -->
<div class="card">
<div class="chd">
<div class="cht"><div class="dot" style="background: var(--red)"></div>涨跌分布</div>
<div class="cht">
<div class="dot" style="background: var(--red)"></div>
涨跌分布
</div>
</div>
<div class="cbd" style="padding: 4px 8px 8px">
<div class="bchart">
@ -91,7 +96,10 @@
<!-- Trend sectors TOP 10 -->
<div class="card">
<div class="chd">
<div class="cht"><div class="dot" style="background: var(--blu)"></div>趋势板块 TOP 10</div>
<div class="cht">
<div class="dot" style="background: var(--blu)"></div>
趋势板块 TOP 10
</div>
</div>
<div class="cbd" style="padding: 0">
<div class="tw" style="max-height: 230px; overflow-y: auto">
@ -108,9 +116,15 @@
</thead>
<tbody>
<tr v-for="s in topSectors" :key="s.name">
<td><span :class="['rb', rankClass(s.rank)]">{{ s.rank }}</span></td>
<td><b>{{ s.name }}</b></td>
<td style="text-align: center"><span :class="['sig', signalClass(s.momentum)]">Rank {{ s.momentum }}</span></td>
<td>
<span :class="['rb', rankClass(s.rank)]">{{ s.rank }}</span>
</td>
<td>
<b>{{ s.name }}</b>
</td>
<td style="text-align: center">
<span :class="['sig', signalClass(s.momentum)]">Rank {{ s.momentum }}</span>
</td>
<td style="text-align: center" :class="{ vr: s.limitUp > 5 }">{{ s.limitUp }}</td>
<td style="text-align: center" :class="{ vr: s.newHigh > 3 }">{{ s.newHigh }}</td>
<td><button class="btn" @click="showDetail(s.name)"></button></td>
@ -127,7 +141,10 @@
<!-- Trend changes -->
<div class="card">
<div class="chd">
<div class="cht"><div class="dot" style="background: var(--org)"></div>趋势变化榜</div>
<div class="cht">
<div class="dot" style="background: var(--org)"></div>
趋势变化榜
</div>
</div>
<div class="cbd">
<div v-for="c in trendChanges" :key="c.name" class="change-item">
@ -143,7 +160,10 @@
<!-- Continuous stats -->
<div class="card">
<div class="chd">
<div class="cht"><div class="dot" style="background: var(--grn)"></div>强势板块连续统计</div>
<div class="cht">
<div class="dot" style="background: var(--grn)"></div>
强势板块连续统计
</div>
</div>
<div class="cbd" style="padding: 0">
<table>
@ -157,8 +177,12 @@
</thead>
<tbody>
<tr v-for="c in continuousStats" :key="c.name">
<td><b>{{ c.name }}</b></td>
<td style="text-align: center"><span :class="['sig', continuousClass(c.days)]">{{ c.days }}</span></td>
<td>
<b>{{ c.name }}</b>
</td>
<td style="text-align: center">
<span :class="['sig', continuousClass(c.days)]">{{ c.days }}</span>
</td>
<td style="text-align: center">{{ c.currentRank }}</td>
<td style="text-align: center">{{ c.avgRank }}</td>
</tr>
@ -178,12 +202,30 @@
</div>
<div class="mbd">
<div class="detailrow">
<div class="stat-card"><div class="sl">当前动量</div><div class="sv">12.34</div></div>
<div class="stat-card"><div class="sl">近5日趋势</div><div class="sv">持续上升</div></div>
<div class="stat-card"><div class="sl">涨停家数</div><div class="sv">3</div></div>
<div class="stat-card"><div class="sl">创新高</div><div class="sv">7</div></div>
<div class="stat-card"><div class="sl">连续天数</div><div class="sv">5</div></div>
<div class="stat-card"><div class="sl">平均排名</div><div class="sv">2.4</div></div>
<div class="stat-card">
<div class="sl">当前动量</div>
<div class="sv">12.34</div>
</div>
<div class="stat-card">
<div class="sl">近5日趋势</div>
<div class="sv">持续上升</div>
</div>
<div class="stat-card">
<div class="sl">涨停家数</div>
<div class="sv">3</div>
</div>
<div class="stat-card">
<div class="sl">创新高</div>
<div class="sv">7</div>
</div>
<div class="stat-card">
<div class="sl">连续天数</div>
<div class="sv">5</div>
</div>
<div class="stat-card">
<div class="sl">平均排名</div>
<div class="sv">2.4</div>
</div>
</div>
<div id="detail-kline" class="kline-container"></div>
</div>
@ -287,7 +329,24 @@ function initDetailChart() {
title: { text: 'K线示意', textStyle: { color: '#e6edf3', fontSize: 11 } },
grid: { left: 50, right: 20, top: 30, bottom: 20 },
xAxis: {
data: ['04-01', '04-02', '04-03', '04-04', '04-05', '04-08', '04-09', '04-10', '04-11', '04-14', '04-15', '04-16', '04-17', '04-18', '04-21', '04-22'],
data: [
'04-01',
'04-02',
'04-03',
'04-04',
'04-05',
'04-08',
'04-09',
'04-10',
'04-11',
'04-14',
'04-15',
'04-16',
'04-17',
'04-18',
'04-21',
'04-22',
],
axisLine: { lineStyle: { color: '#30363d' } },
axisLabel: { color: '#8b949e' },
},
@ -295,17 +354,29 @@ function initDetailChart() {
splitLine: { lineStyle: { color: '#21262d' } },
axisLabel: { color: '#8b949e' },
},
series: [{
type: 'candlestick',
data: [
[10, 11, 9.5, 10.5], [10.5, 11.5, 10, 11], [11, 12, 10.5, 11.5],
[11.5, 12.5, 11, 12], [12, 13, 11.5, 12.5], [12.5, 13.5, 12, 13],
[13, 14, 12.5, 13.5], [13.5, 14.5, 13, 14], [14, 15, 13.5, 14.5],
[14.5, 15.5, 14, 15], [15, 16, 14.5, 15.5], [15.5, 16.5, 15, 16],
[16, 17, 15.5, 16.5], [16.5, 17.5, 16, 17], [17, 18, 16.5, 17.5],
[17.5, 18.5, 17, 18],
],
}],
series: [
{
type: 'candlestick',
data: [
[10, 11, 9.5, 10.5],
[10.5, 11.5, 10, 11],
[11, 12, 10.5, 11.5],
[11.5, 12.5, 11, 12],
[12, 13, 11.5, 12.5],
[12.5, 13.5, 12, 13],
[13, 14, 12.5, 13.5],
[13.5, 14.5, 13, 14],
[14, 15, 13.5, 14.5],
[14.5, 15.5, 14, 15],
[15, 16, 14.5, 15.5],
[15.5, 16.5, 15, 16],
[16, 17, 15.5, 16.5],
[16.5, 17.5, 16, 17],
[17, 18, 16.5, 17.5],
[17.5, 18.5, 17, 18],
],
},
],
})
}
</script>
@ -417,14 +488,33 @@ function initDetailChart() {
height: 2px;
}
&.hot::before { background: linear-gradient(90deg, #ff8a65, #f44336); }
&.warm::before { background: linear-gradient(90deg, #ffd54f, #ff8a65); }
&.neutral::before { background: linear-gradient(90deg, #66bb6a, #ffd54f); }
&.cool::before { background: linear-gradient(90deg, #4fc3f7, #66bb6a); }
&.hot::before {
background: linear-gradient(90deg, #ff8a65, #f44336);
}
&.warm::before {
background: linear-gradient(90deg, #ffd54f, #ff8a65);
}
&.neutral::before {
background: linear-gradient(90deg, #66bb6a, #ffd54f);
}
&.cool::before {
background: linear-gradient(90deg, #4fc3f7, #66bb6a);
}
.snm { font-size: 11px; color: var(--t2); margin-bottom: 4px; }
.sval { font-size: 28px; font-weight: 700; line-height: 1; }
.slabel { font-size: 10px; margin-top: 2px; }
.snm {
font-size: 11px;
color: var(--t2);
margin-bottom: 4px;
}
.sval {
font-size: 28px;
font-weight: 700;
line-height: 1;
}
.slabel {
font-size: 10px;
margin-top: 2px;
}
.sbar {
margin-top: 8px;
@ -437,10 +527,18 @@ function initDetailChart() {
height: 100%;
border-radius: 2px;
&.sf-h { background: linear-gradient(90deg, #ff8a65, #f44336); }
&.sf-w { background: linear-gradient(90deg, #ffd54f, #ff8a65); }
&.sf-n { background: linear-gradient(90deg, #66bb6a, #ffd54f); }
&.sf-c { background: linear-gradient(90deg, #4fc3f7, #66bb6a); }
&.sf-h {
background: linear-gradient(90deg, #ff8a65, #f44336);
}
&.sf-w {
background: linear-gradient(90deg, #ffd54f, #ff8a65);
}
&.sf-n {
background: linear-gradient(90deg, #66bb6a, #ffd54f);
}
&.sf-c {
background: linear-gradient(90deg, #4fc3f7, #66bb6a);
}
}
}
@ -520,13 +618,25 @@ function initDetailChart() {
width: 18px;
border-radius: 2px 2px 0 0;
&.bar-r { background: var(--red); }
&.bar-g { background: var(--grn); }
&.bar-gr { background: var(--tm); }
&.bar-r {
background: var(--red);
}
&.bar-g {
background: var(--grn);
}
&.bar-gr {
background: var(--tm);
}
}
.blbl { font-size: 7px; color: var(--tm); }
.bval { font-size: 7px; color: var(--t2); }
.blbl {
font-size: 7px;
color: var(--tm);
}
.bval {
font-size: 7px;
color: var(--t2);
}
}
table {
@ -569,14 +679,30 @@ function initDetailChart() {
font-size: 9px;
font-weight: 700;
&.r1 { background: linear-gradient(135deg, #ff6b6b, #ee5a24); color: #fff; }
&.r2 { background: linear-gradient(135deg, #ffa502, #e17009); color: #fff; }
&.r3 { background: linear-gradient(135deg, #e3b341, #d29922); color: #fff; }
&.ro { background: var(--bg1); color: var(--t2); }
&.r1 {
background: linear-gradient(135deg, #ff6b6b, #ee5a24);
color: #fff;
}
&.r2 {
background: linear-gradient(135deg, #ffa502, #e17009);
color: #fff;
}
&.r3 {
background: linear-gradient(135deg, #e3b341, #d29922);
color: #fff;
}
&.ro {
background: var(--bg1);
color: var(--t2);
}
}
.vr { color: var(--red); }
.vg { color: var(--grn); }
.vr {
color: var(--red);
}
.vg {
color: var(--grn);
}
.sig {
display: inline-flex;
@ -585,15 +711,32 @@ function initDetailChart() {
font-size: 9px;
font-weight: 600;
&.sig-u { background: rgba(248, 81, 73, 0.12); color: var(--red); }
&.sig-o { background: rgba(210, 153, 34, 0.12); color: var(--org); }
&.sig-n { background: var(--bg3); color: var(--tm); }
&.sig-u {
background: rgba(248, 81, 73, 0.12);
color: var(--red);
}
&.sig-o {
background: rgba(210, 153, 34, 0.12);
color: var(--org);
}
&.sig-n {
background: var(--bg3);
color: var(--tm);
}
}
.shot { color: #f44336; }
.swarm { color: #ff8a65; }
.sneutral { color: #ffd54f; }
.scool { color: #66bb6a; }
.shot {
color: #f44336;
}
.swarm {
color: #ff8a65;
}
.sneutral {
color: #ffd54f;
}
.scool {
color: #66bb6a;
}
.change-item {
display: flex;
@ -602,15 +745,33 @@ function initDetailChart() {
padding: 4px 0;
border-bottom: 1px solid var(--bd);
&:last-child { border-bottom: none; }
&:last-child {
border-bottom: none;
}
.name { flex: 1; font-size: 11px; }
.rk { font-size: 10px; color: var(--t2); min-width: 60px; }
.delta { font-size: 10px; font-weight: 600; min-width: 50px; text-align: right; }
.name {
flex: 1;
font-size: 11px;
}
.rk {
font-size: 10px;
color: var(--t2);
min-width: 60px;
}
.delta {
font-size: 10px;
font-weight: 600;
min-width: 50px;
text-align: right;
}
}
.arrow-up { color: var(--red); }
.arrow-down { color: var(--grn); }
.arrow-up {
color: var(--red);
}
.arrow-down {
color: var(--grn);
}
.btn {
padding: 3px 8px;
@ -621,7 +782,9 @@ function initDetailChart() {
font-size: 9px;
cursor: pointer;
&:hover { background: var(--bg3); }
&:hover {
background: var(--bg3);
}
}
}
</style>
@ -656,7 +819,10 @@ function initDetailChart() {
align-items: center;
justify-content: space-between;
h3 { font-size: 13px; color: var(--t1); }
h3 {
font-size: 13px;
color: var(--t1);
}
}
.mcl {
@ -672,7 +838,10 @@ function initDetailChart() {
align-items: center;
justify-content: center;
&:hover { background: var(--red); color: #fff; }
&:hover {
background: var(--red);
color: #fff;
}
}
.mbd {
@ -695,8 +864,16 @@ function initDetailChart() {
border-radius: 5px;
padding: 8px 12px;
.sl { font-size: 9px; color: var(--tm); margin-bottom: 2px; }
.sv { font-size: 22px; font-weight: 700; color: var(--t1); }
.sl {
font-size: 9px;
color: var(--tm);
margin-bottom: 2px;
}
.sv {
font-size: 22px;
font-weight: 700;
color: var(--t1);
}
}
.kline-container {

@ -30,7 +30,9 @@
<table class="hmt">
<thead>
<tr>
<th style="width: 80px; text-align: left; padding: 4px 10px; font-size: 10px">2级行业</th>
<th style="width: 80px; text-align: left; padding: 4px 10px; font-size: 10px">
2级行业
</th>
<th v-for="d in dates" :key="d">{{ d }}</th>
</tr>
</thead>
@ -76,8 +78,15 @@
</thead>
<tbody>
<tr v-for="row in heatmapData" :key="row.industry">
<td><b>{{ row.industry }}</b></td>
<td v-for="(rank, idx) in row.ranks" :key="idx" style="text-align: center" :class="rankColorClass(rank, row.ranks)">
<td>
<b>{{ row.industry }}</b>
</td>
<td
v-for="(rank, idx) in row.ranks"
:key="idx"
style="text-align: center"
:class="rankColorClass(rank, row.ranks)"
>
{{ rank }}
</td>
<td :class="trendClass(row)">
@ -101,12 +110,30 @@
</div>
<div class="mbd">
<div class="detailrow">
<div class="stat-card"><div class="sl">当前动量</div><div class="sv">12.34</div></div>
<div class="stat-card"><div class="sl">近5日趋势</div><div class="sv">持续上升</div></div>
<div class="stat-card"><div class="sl">涨停家数</div><div class="sv">3</div></div>
<div class="stat-card"><div class="sl">创新高</div><div class="sv">7</div></div>
<div class="stat-card"><div class="sl">连续天数</div><div class="sv">5</div></div>
<div class="stat-card"><div class="sl">平均排名</div><div class="sv">2.4</div></div>
<div class="stat-card">
<div class="sl">当前动量</div>
<div class="sv">12.34</div>
</div>
<div class="stat-card">
<div class="sl">近5日趋势</div>
<div class="sv">持续上升</div>
</div>
<div class="stat-card">
<div class="sl">涨停家数</div>
<div class="sv">3</div>
</div>
<div class="stat-card">
<div class="sl">创新高</div>
<div class="sv">7</div>
</div>
<div class="stat-card">
<div class="sl">连续天数</div>
<div class="sv">5</div>
</div>
<div class="stat-card">
<div class="sl">平均排名</div>
<div class="sv">2.4</div>
</div>
</div>
<div id="heatmap-kline" class="kline-container"></div>
</div>
@ -129,7 +156,18 @@ const selectedDate = ref('2025-04-22')
const viewMode = ref<'hm' | 'tb'>('hm')
// Dates
const dates = ['04-22', '04-21', '04-18', '04-17', '04-16', '04-15', '04-14', '04-11', '04-10', '04-09']
const dates = [
'04-22',
'04-21',
'04-18',
'04-17',
'04-16',
'04-15',
'04-14',
'04-11',
'04-10',
'04-09',
]
// Heatmap data
interface HeatmapRow {
@ -141,25 +179,90 @@ interface HeatmapRow {
const heatmapData = ref<HeatmapRow[]>([
{ industry: '零售', ranks: [1, 1, 1, 3, 8, 10, 2, 5, 8, 8], trend: -7, trendText: '下降 7位' },
{ industry: '化学制药', ranks: [2, 4, 5, 5, 6, 2, 3, 3, 7, 11], trend: -9, trendText: '下降 9位' },
{
industry: '化学制药',
ranks: [2, 4, 5, 5, 6, 2, 3, 3, 7, 11],
trend: -9,
trendText: '下降 9位',
},
{ industry: '食品', ranks: [3, 2, 2, 1, 2, 5, 5, 2, 2, 2], trend: 1, trendText: '上升 1位' },
{ industry: '畜牧业', ranks: [4, 3, 9, 4, 4, 1, 4, 7, 5, 3], trend: 1, trendText: '上升 1位' },
{ industry: '贵金属', ranks: [5, 6, 4, 2, 3, 6, 10, 1, 6, 6], trend: -1, trendText: '下降 1位' },
{ industry: '生物医药', ranks: [6, 5, 6, 7, 5, 4, 1, 4, 3, 5], trend: 1, trendText: '上升 1位' },
{ industry: '物流', ranks: [7, 11, 15, 12, 18, 14, 12, 10, 12, 15], trend: -8, trendText: '下降 8位' },
{
industry: '物流',
ranks: [7, 11, 15, 12, 18, 14, 12, 10, 12, 15],
trend: -8,
trendText: '下降 8位',
},
{ industry: '半导体', ranks: [8, 7, 7, 6, 7, 3, 6, 8, 4, 4], trend: 4, trendText: '上升 4位' },
{ industry: '化学制品', ranks: [9, 8, 3, 8, 10, 8, 3, 6, 9, 9], trend: 0, trendText: '下降 0位' },
{ industry: '商业物业', ranks: [10, 9, 8, 10, 9, 7, 7, 15, 11, 12], trend: -2, trendText: '下降 2位' },
{ industry: '软件开发', ranks: [15, 12, 10, 9, 11, 9, 8, 9, 10, 10], trend: 5, trendText: '上升 5位' },
{ industry: '新能源', ranks: [20, 18, 14, 11, 12, 11, 9, 11, 14, 13], trend: 7, trendText: '上升 7位' },
{ industry: '传媒', ranks: [25, 22, 18, 15, 14, 12, 11, 12, 15, 14], trend: 11, trendText: '上升 11位' },
{ industry: '银行', ranks: [12, 14, 12, 14, 15, 13, 14, 13, 13, 11], trend: 1, trendText: '上升 1位' },
{ industry: '房地产', ranks: [18, 20, 22, 18, 16, 18, 15, 14, 18, 16], trend: 2, trendText: '上升 2位' },
{ industry: '汽车', ranks: [14, 15, 16, 16, 13, 15, 13, 18, 16, 18], trend: -4, trendText: '下降 4位' },
{ industry: '电子器件', ranks: [16, 16, 11, 13, 20, 16, 16, 16, 20, 17], trend: -1, trendText: '下降 1位' },
{ industry: '饮料', ranks: [11, 10, 13, 20, 22, 20, 18, 17, 17, 19], trend: -8, trendText: '下降 8位' },
{ industry: '建筑装饰', ranks: [22, 19, 20, 22, 25, 22, 20, 19, 22, 20], trend: 2, trendText: '上升 2位' },
{ industry: '通信', ranks: [30, 28, 25, 25, 28, 25, 22, 20, 25, 22], trend: 8, trendText: '上升 8位' },
{
industry: '商业物业',
ranks: [10, 9, 8, 10, 9, 7, 7, 15, 11, 12],
trend: -2,
trendText: '下降 2位',
},
{
industry: '软件开发',
ranks: [15, 12, 10, 9, 11, 9, 8, 9, 10, 10],
trend: 5,
trendText: '上升 5位',
},
{
industry: '新能源',
ranks: [20, 18, 14, 11, 12, 11, 9, 11, 14, 13],
trend: 7,
trendText: '上升 7位',
},
{
industry: '传媒',
ranks: [25, 22, 18, 15, 14, 12, 11, 12, 15, 14],
trend: 11,
trendText: '上升 11位',
},
{
industry: '银行',
ranks: [12, 14, 12, 14, 15, 13, 14, 13, 13, 11],
trend: 1,
trendText: '上升 1位',
},
{
industry: '房地产',
ranks: [18, 20, 22, 18, 16, 18, 15, 14, 18, 16],
trend: 2,
trendText: '上升 2位',
},
{
industry: '汽车',
ranks: [14, 15, 16, 16, 13, 15, 13, 18, 16, 18],
trend: -4,
trendText: '下降 4位',
},
{
industry: '电子器件',
ranks: [16, 16, 11, 13, 20, 16, 16, 16, 20, 17],
trend: -1,
trendText: '下降 1位',
},
{
industry: '饮料',
ranks: [11, 10, 13, 20, 22, 20, 18, 17, 17, 19],
trend: -8,
trendText: '下降 8位',
},
{
industry: '建筑装饰',
ranks: [22, 19, 20, 22, 25, 22, 20, 19, 22, 20],
trend: 2,
trendText: '上升 2位',
},
{
industry: '通信',
ranks: [30, 28, 25, 25, 28, 25, 22, 20, 25, 22],
trend: 8,
trendText: '上升 8位',
},
])
// Modal
@ -202,7 +305,24 @@ function initDetailChart() {
title: { text: 'K线示意', textStyle: { color: '#e6edf3', fontSize: 11 } },
grid: { left: 50, right: 20, top: 30, bottom: 20 },
xAxis: {
data: ['04-01', '04-02', '04-03', '04-04', '04-05', '04-08', '04-09', '04-10', '04-11', '04-14', '04-15', '04-16', '04-17', '04-18', '04-21', '04-22'],
data: [
'04-01',
'04-02',
'04-03',
'04-04',
'04-05',
'04-08',
'04-09',
'04-10',
'04-11',
'04-14',
'04-15',
'04-16',
'04-17',
'04-18',
'04-21',
'04-22',
],
axisLine: { lineStyle: { color: '#30363d' } },
axisLabel: { color: '#8b949e' },
},
@ -210,17 +330,29 @@ function initDetailChart() {
splitLine: { lineStyle: { color: '#21262d' } },
axisLabel: { color: '#8b949e' },
},
series: [{
type: 'candlestick',
data: [
[10, 11, 9.5, 10.5], [10.5, 11.5, 10, 11], [11, 12, 10.5, 11.5],
[11.5, 12.5, 11, 12], [12, 13, 11.5, 12.5], [12.5, 13.5, 12, 13],
[13, 14, 12.5, 13.5], [13.5, 14.5, 13, 14], [14, 15, 13.5, 14.5],
[14.5, 15.5, 14, 15], [15, 16, 14.5, 15.5], [15.5, 16.5, 15, 16],
[16, 17, 15.5, 16.5], [16.5, 17.5, 16, 17], [17, 18, 16.5, 17.5],
[17.5, 18.5, 17, 18],
],
}],
series: [
{
type: 'candlestick',
data: [
[10, 11, 9.5, 10.5],
[10.5, 11.5, 10, 11],
[11, 12, 10.5, 11.5],
[11.5, 12.5, 11, 12],
[12, 13, 11.5, 12.5],
[12.5, 13.5, 12, 13],
[13, 14, 12.5, 13.5],
[13.5, 14.5, 13, 14],
[14, 15, 13.5, 14.5],
[14.5, 15.5, 14, 15],
[15, 16, 14.5, 15.5],
[15.5, 16.5, 15, 16],
[16, 17, 15.5, 16.5],
[16.5, 17.5, 16, 17],
[17, 18, 16.5, 17.5],
[17.5, 18.5, 17, 18],
],
},
],
})
}
</script>
@ -316,7 +448,9 @@ function initDetailChart() {
padding: 4px 2px;
min-width: 44px;
cursor: pointer;
transition: transform 0.1s, box-shadow 0.1s;
transition:
transform 0.1s,
box-shadow 0.1s;
&:hover {
transform: scale(1.06);
@ -330,17 +464,50 @@ function initDetailChart() {
}
}
.h1 { background: linear-gradient(135deg, #ff4444, #cc0000); color: #fff; }
.h2 { background: linear-gradient(135deg, #ff6b6b, #e04040); color: #fff; }
.h3 { background: linear-gradient(135deg, #ff9a76, #d94f4f); color: #fff; }
.h4 { background: linear-gradient(135deg, #ffa502, #c77f00); color: #fff; }
.h5 { background: linear-gradient(135deg, #e3b341, #b8911a); color: #333; }
.h6 { background: linear-gradient(135deg, #e6d34a, #c4b538); color: #333; }
.h7 { background: linear-gradient(135deg, #8bda5e, #5fb32e); color: #fff; }
.h8 { background: linear-gradient(135deg, #4dd08b, #28a360); color: #fff; }
.h9 { background: linear-gradient(135deg, #36b37e, #1e8c5e); color: #fff; }
.h10 { background: linear-gradient(135deg, #2d9cdb, #1a7ab5); color: #fff; }
.hd { background: var(--bg2); color: var(--t2); }
.h1 {
background: linear-gradient(135deg, #ff4444, #cc0000);
color: #fff;
}
.h2 {
background: linear-gradient(135deg, #ff6b6b, #e04040);
color: #fff;
}
.h3 {
background: linear-gradient(135deg, #ff9a76, #d94f4f);
color: #fff;
}
.h4 {
background: linear-gradient(135deg, #ffa502, #c77f00);
color: #fff;
}
.h5 {
background: linear-gradient(135deg, #e3b341, #b8911a);
color: #333;
}
.h6 {
background: linear-gradient(135deg, #e6d34a, #c4b538);
color: #333;
}
.h7 {
background: linear-gradient(135deg, #8bda5e, #5fb32e);
color: #fff;
}
.h8 {
background: linear-gradient(135deg, #4dd08b, #28a360);
color: #fff;
}
.h9 {
background: linear-gradient(135deg, #36b37e, #1e8c5e);
color: #fff;
}
.h10 {
background: linear-gradient(135deg, #2d9cdb, #1a7ab5);
color: #fff;
}
.hd {
background: var(--bg2);
color: var(--t2);
}
.hnc {
background: var(--bg2);
@ -375,25 +542,56 @@ function initDetailChart() {
height: 12px;
border-radius: 2px;
&.h1 { background: linear-gradient(135deg, #ff4444, #cc0000); }
&.h2 { background: linear-gradient(135deg, #ff6b6b, #e04040); }
&.h3 { background: linear-gradient(135deg, #ff9a76, #d94f4f); }
&.h4 { background: linear-gradient(135deg, #ffa502, #c77f00); }
&.h5 { background: linear-gradient(135deg, #e3b341, #b8911a); }
&.h6 { background: linear-gradient(135deg, #e6d34a, #c4b538); }
&.h7 { background: linear-gradient(135deg, #8bda5e, #5fb32e); }
&.h8 { background: linear-gradient(135deg, #4dd08b, #28a360); }
&.h9 { background: linear-gradient(135deg, #36b37e, #1e8c5e); }
&.h10 { background: linear-gradient(135deg, #2d9cdb, #1a7ab5); }
&.hd { background: var(--bg2); border: 1px solid var(--bd); }
&.h1 {
background: linear-gradient(135deg, #ff4444, #cc0000);
}
&.h2 {
background: linear-gradient(135deg, #ff6b6b, #e04040);
}
&.h3 {
background: linear-gradient(135deg, #ff9a76, #d94f4f);
}
&.h4 {
background: linear-gradient(135deg, #ffa502, #c77f00);
}
&.h5 {
background: linear-gradient(135deg, #e3b341, #b8911a);
}
&.h6 {
background: linear-gradient(135deg, #e6d34a, #c4b538);
}
&.h7 {
background: linear-gradient(135deg, #8bda5e, #5fb32e);
}
&.h8 {
background: linear-gradient(135deg, #4dd08b, #28a360);
}
&.h9 {
background: linear-gradient(135deg, #36b37e, #1e8c5e);
}
&.h10 {
background: linear-gradient(135deg, #2d9cdb, #1a7ab5);
}
&.hd {
background: var(--bg2);
border: 1px solid var(--bd);
}
}
}
.vr { color: var(--red); }
.vg { color: var(--grn); }
.vr {
color: var(--red);
}
.vg {
color: var(--grn);
}
.arrow-up { color: var(--red); }
.arrow-down { color: var(--grn); }
.arrow-up {
color: var(--red);
}
.arrow-down {
color: var(--grn);
}
.enhanced-tbl {
width: 100%;
@ -457,7 +655,10 @@ function initDetailChart() {
align-items: center;
justify-content: space-between;
h3 { font-size: 13px; color: var(--t1); }
h3 {
font-size: 13px;
color: var(--t1);
}
}
.mcl {
@ -473,7 +674,10 @@ function initDetailChart() {
align-items: center;
justify-content: center;
&:hover { background: var(--red); color: #fff; }
&:hover {
background: var(--red);
color: #fff;
}
}
.mbd {
@ -496,8 +700,16 @@ function initDetailChart() {
border-radius: 5px;
padding: 8px 12px;
.sl { font-size: 9px; color: var(--tm); margin-bottom: 2px; }
.sv { font-size: 22px; font-weight: 700; color: var(--t1); }
.sl {
font-size: 9px;
color: var(--tm);
margin-bottom: 2px;
}
.sv {
font-size: 22px;
font-weight: 700;
color: var(--t1);
}
}
.kline-container {

@ -25,7 +25,13 @@
/>
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" style="width: 100%" :loading="loading" @click="handleLogin">
<el-button
type="primary"
size="large"
style="width: 100%"
:loading="loading"
@click="handleLogin"
>
登录
</el-button>
</el-form-item>

@ -1,10 +1,45 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="股票名称" prop="name"><el-input v-model="queryParams.name" placeholder="请输入股票名称" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="东财行业2级" prop="blemind2"><el-input v-model="queryParams.blemind2" placeholder="请输入所属东财行业指数2级" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="东财行业3级" prop="blemind3"><el-input v-model="queryParams.blemind3" placeholder="请输入所属东财行业指数3级" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="首发上市日期" prop="listdate"><el-date-picker v-model="queryParams.listdate" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择首发上市日期" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="股票名称" prop="name"
><el-input
v-model="queryParams.name"
placeholder="请输入股票名称"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="东财行业2级" prop="blemind2"
><el-input
v-model="queryParams.blemind2"
placeholder="请输入所属东财行业指数2级"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="东财行业3级" prop="blemind3"
><el-input
v-model="queryParams.blemind3"
placeholder="请输入所属东财行业指数3级"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="首发上市日期" prop="listdate"
><el-date-picker
v-model="queryParams.listdate"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择首发上市日期"
/></el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
<el-button size="small" @click="resetQuery"></el-button>
@ -12,10 +47,48 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['stocksystem:stockbasic:add']"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['stocksystem:stockbasic:edit']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['stocksystem:stockbasic:remove']"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['stocksystem:stockbasic:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['stocksystem:stockbasic:add']"
>新增</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['stocksystem:stockbasic:edit']"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['stocksystem:stockbasic:remove']"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['stocksystem:stockbasic:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -28,14 +101,37 @@
<el-table-column label="首发上市日期" align="center" prop="listdate" width="180" />
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="股票名称" prop="name"><el-input v-model="form.name" placeholder="请输入股票名称" /></el-form-item>
<el-form-item label="东财行业2级" prop="blemind2"><el-input v-model="form.blemind2" placeholder="请输入所属东财行业指数2级" /></el-form-item>
<el-form-item label="东财行业3级" prop="blemind3"><el-input v-model="form.blemind3" placeholder="请输入所属东财行业指数3级" /></el-form-item>
<el-form-item label="首发上市日期" prop="listdate"><el-date-picker v-model="form.listdate" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择首发上市日期" /></el-form-item>
<el-form-item label="股票名称" prop="name"
><el-input v-model="form.name" placeholder="请输入股票名称"
/></el-form-item>
<el-form-item label="东财行业2级" prop="blemind2"
><el-input v-model="form.blemind2" placeholder="请输入所属东财行业指数2级"
/></el-form-item>
<el-form-item label="东财行业3级" prop="blemind3"
><el-input v-model="form.blemind3" placeholder="请输入所属东财行业指数3级"
/></el-form-item>
<el-form-item label="首发上市日期" prop="listdate"
><el-date-picker
v-model="form.listdate"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择首发上市日期"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -47,8 +143,15 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
import { getStockbasicList, getStockbasic, delStockbasic, addStockbasic, updateStockbasic } from '@/api/modules/stocksystem/stockbasic'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import {
getStockbasicList,
getStockbasic,
delStockbasic,
addStockbasic,
updateStockbasic,
} from '@/api/modules/stocksystem/stockbasic'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -62,26 +165,103 @@ const ids = ref<any[]>([])
const title = ref('')
const open = ref(false)
const queryParams = reactive({ pageNum: 1, pageSize: 10, name: '', blemind2: '', blemind3: '', listdate: '' })
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
name: '',
blemind2: '',
blemind3: '',
listdate: '',
})
const form = ref<any>({ id: null, code: '', name: '', blemind2: '', blemind3: '', listdate: '' })
const rules = {}
async function getList() { loading.value = true; try { const res = await getStockbasicList(queryParams); stockbasicList.value = res.rows; total.value = res.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { id: null, code: '', name: '', blemind2: '', blemind3: '', listdate: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加基础数据' }
async function handleUpdate(row?: any) { reset(); const id = row?.id || ids.value[0]; const res = await getStockbasic(id); form.value = res.data; open.value = true; title.value = '修改基础数据' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.id != null) { await updateStockbasic(form.value); ElMessage.success('修改成功') } else { await addStockbasic(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const idList = row?.id || ids.value; ElMessageBox.confirm(`是否确认删除基础数据编号为"${idList}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delStockbasic(idList); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/stocksystem/stockbasic/export`, '_blank') }
async function getList() {
loading.value = true
try {
const res: any = await getStockbasicList(queryParams)
stockbasicList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = { id: null, code: '', name: '', blemind2: '', blemind3: '', listdate: '' }
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加基础数据'
}
async function handleUpdate(row?: any) {
reset()
const id = row?.id || ids.value[0]
const res = await getStockbasic(id)
form.value = res.data
open.value = true
title.value = '修改基础数据'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.id != null) {
await updateStockbasic(form.value)
ElMessage.success('修改成功')
} else {
await addStockbasic(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const idList = row?.id || ids.value
ElMessageBox.confirm(`是否确认删除基础数据编号为"${idList}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delStockbasic(idList)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/stocksystem/stockbasic/export`, '_blank')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,9 +1,37 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="指数代码" prop="code"><el-input v-model="queryParams.code" placeholder="请输入指数代码" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="指数名称" prop="name"><el-input v-model="queryParams.name" placeholder="请输入指数名称" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="指数代码" prop="code"
><el-input
v-model="queryParams.code"
placeholder="请输入指数代码"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="指数名称" prop="name"
><el-input
v-model="queryParams.name"
placeholder="请输入指数名称"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/></el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
<el-button size="small" @click="resetQuery"></el-button>
@ -11,7 +39,16 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['stocksystem:stockindex:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['stocksystem:stockindex:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -41,7 +78,16 @@
<el-table-column label="近期新低" align="center" prop="rcntlow" />
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
</div>
</template>
@ -60,16 +106,43 @@ const ids = ref<any[]>([])
const queryParams = reactive({ pageNum: 1, pageSize: 10, code: '', name: '', tradeDay: '' })
async function getList() { loading.value = true; try { const res = await getStockindexList(queryParams); stockindexList.value = res.rows; total.value = res.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/stocksystem/stockindex/export`, '_blank') }
async function getList() {
loading.value = true
try {
const res: any = await getStockindexList(queryParams)
stockindexList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/stocksystem/stockindex/export`, '_blank')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,13 +1,69 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="股票代码" prop="code"><el-input v-model="queryParams.code" placeholder="请输入股票代码" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"><el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" /></el-form-item>
<el-form-item label="可交易日数" prop="tradeDays"><el-input v-model="queryParams.tradeDays" placeholder="请输入可交易日数" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="10日涨跌幅" prop="differrange10"><el-input v-model="queryParams.differrange10" placeholder="请输入10日区间涨跌幅" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="20日涨跌幅" prop="differrange20"><el-input v-model="queryParams.differrange20" placeholder="请输入20日区间涨跌幅" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="60日涨跌幅" prop="differrange60"><el-input v-model="queryParams.differrange60" placeholder="请输入60日区间涨跌幅" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="机构持仓" prop="agenciesHold"><el-input v-model="queryParams.agenciesHold" placeholder="请输入机构持仓合计" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="股票代码" prop="code"
><el-input
v-model="queryParams.code"
placeholder="请输入股票代码"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="交易日期" prop="tradeDay"
><el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
/></el-form-item>
<el-form-item label="可交易日数" prop="tradeDays"
><el-input
v-model="queryParams.tradeDays"
placeholder="请输入可交易日数"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="10日涨跌幅" prop="differrange10"
><el-input
v-model="queryParams.differrange10"
placeholder="请输入10日区间涨跌幅"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="20日涨跌幅" prop="differrange20"
><el-input
v-model="queryParams.differrange20"
placeholder="请输入20日区间涨跌幅"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="60日涨跌幅" prop="differrange60"
><el-input
v-model="queryParams.differrange60"
placeholder="请输入60日区间涨跌幅"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="机构持仓" prop="agenciesHold"
><el-input
v-model="queryParams.agenciesHold"
placeholder="请输入机构持仓合计"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
<el-button size="small" @click="resetQuery"></el-button>
@ -15,7 +71,16 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['stocksystem:stocks:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['stocksystem:stocks:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -38,7 +103,16 @@
<el-table-column label="机构持仓" align="center" prop="agenciesHold" />
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
</div>
</template>
@ -55,18 +129,55 @@ const multiple = ref(true)
const showSearch = ref(true)
const ids = ref<any[]>([])
const queryParams = reactive({ pageNum: 1, pageSize: 10, code: '', tradeDay: '', tradeDays: '', differrange10: '', differrange20: '', differrange60: '', agenciesHold: '' })
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
code: '',
tradeDay: '',
tradeDays: '',
differrange10: '',
differrange20: '',
differrange60: '',
agenciesHold: '',
})
async function getList() { loading.value = true; try { const res: any = await getStocksList(queryParams); stocksList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.id); single.value = selection.length !== 1; multiple.value = !selection.length }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/stocksystem/stocks/export`, '_blank') }
async function getList() {
loading.value = true
try {
const res: any = await getStocksList(queryParams)
stocksList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/stocksystem/stocks/export`, '_blank')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,11 +1,30 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="交易日期" prop="tradeDay">
<el-date-picker v-model="queryParams.tradeDay" clearable size="small" type="date" value-format="YYYY-MM-DD" placeholder="选择交易日期" @change="handleQuery" />
<el-date-picker
v-model="queryParams.tradeDay"
clearable
size="small"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择交易日期"
@change="handleQuery"
/>
</el-form-item>
<el-form-item label="动量周期" prop="type">
<el-select v-model="queryParams.type" placeholder="动量周期" size="small" @change="handleQuery">
<el-select
v-model="queryParams.type"
placeholder="动量周期"
size="small"
@change="handleQuery"
>
<el-option label="1日" :value="1" />
<el-option label="3日" :value="3" />
<el-option label="5日" :value="5" />
@ -16,7 +35,13 @@
</el-select>
</el-form-item>
<el-form-item label="板块名称" prop="blemind2">
<el-input v-model="queryParams.blemind2" placeholder="请输入板块名称" clearable size="small" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.blemind2"
placeholder="请输入板块名称"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button size="small" @click="resetQuery"></el-button>
@ -28,27 +53,53 @@
<div class="tab-header">
<span class="tab-title">{{ queryParams.type }}日趋势板块</span>
</div>
<el-table v-loading="loading" :data="trendData" style="width: 100%" height="560" @cell-click="handleCellClick">
<el-table-column v-for="col in trendColumns" :key="col.prop" :prop="col.prop" :label="col.label" />
<el-table
v-loading="loading"
:data="trendData"
style="width: 100%"
height="560"
@cell-click="handleCellClick"
>
<el-table-column
v-for="col in trendColumns"
:key="col.prop"
:prop="col.prop"
:label="col.label"
/>
</el-table>
</el-tab-pane>
<el-tab-pane label="成交量趋势" name="volume">
<el-tabs v-model="volumeSubTab">
<el-tab-pane label="排名" name="rank">
<el-table v-loading="loading" :data="volumeData" style="width: 100%" height="560">
<el-table-column v-for="col in volumeColumns" :key="col.prop" :prop="col.prop" :label="col.label" />
<el-table-column
v-for="col in volumeColumns"
:key="col.prop"
:prop="col.prop"
:label="col.label"
/>
</el-table>
</el-tab-pane>
<el-tab-pane label="成交量乘数" name="raw">
<el-table v-loading="loading" :data="volumeRawData" style="width: 100%" height="560">
<el-table-column v-for="col in volumeColumns" :key="col.prop" :prop="col.prop" :label="col.label" />
<el-table-column
v-for="col in volumeColumns"
:key="col.prop"
:prop="col.prop"
:label="col.label"
/>
</el-table>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
<el-tab-pane label="涨停板趋势" name="limitUp">
<el-table v-loading="loading" :data="limitUpData" style="width: 100%" height="560">
<el-table-column v-for="col in limitColumns" :key="col.prop" :prop="col.prop" :label="col.label" />
<el-table-column
v-for="col in limitColumns"
:key="col.prop"
:prop="col.prop"
:label="col.label"
/>
</el-table>
</el-tab-pane>
</el-tabs>
@ -57,7 +108,11 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { getTrendsSection, getTradeVolume, getStockIndexLimitUp } from '@/api/modules/stocksystem/trends'
import {
getTrendsSection,
getTradeVolume,
getStockIndexLimitUp,
} from '@/api/modules/stocksystem/trends'
const queryFormRef = ref()
const loading = ref(false)
@ -68,21 +123,26 @@ const volumeSubTab = ref('rank')
const queryParams = reactive({ pageNum: 1, pageSize: 10, tradeDay: '', blemind2: '', type: '20' })
const trendData = ref<any[]>([])
const trendColumns = ref<{prop: string; label: string}[]>([])
const trendColumns = ref<{ prop: string; label: string }[]>([])
const volumeData = ref<any[]>([])
const volumeColumns = ref<{prop: string; label: string}[]>([])
const volumeColumns = ref<{ prop: string; label: string }[]>([])
const volumeRawData = ref<any[]>([])
const limitUpData = ref<any[]>([])
const limitColumns = ref<{prop: string; label: string}[]>([])
const limitColumns = ref<{ prop: string; label: string }[]>([])
function transformToColumnsAndRows(data: any[]) {
if (!data || data.length === 0) return { columns: [], rows: [] }
const columns = data.map((item: any) => ({ prop: item.title || item.key, label: item.title || item.key }))
const columns = data.map((item: any) => ({
prop: item.title || item.key,
label: item.title || item.key,
}))
const maxLen = Math.max(...data.map((item: any) => (item.sortDataList || []).length), 0)
const rows: any[] = []
for (let i = 0; i < maxLen; i++) {
const row: any = {}
data.forEach((item: any) => { row[item.title || item.key] = item.sortDataList?.[i] ?? '' })
data.forEach((item: any) => {
row[item.title || item.key] = item.sortDataList?.[i] ?? ''
})
rows.push(row)
}
return { columns, rows }
@ -91,11 +151,11 @@ function transformToColumnsAndRows(data: any[]) {
async function getList() {
loading.value = true
try {
const [trendRes, volumeRes, limitRes] = await Promise.all([
const [trendRes, volumeRes, limitRes] = (await Promise.all([
getTrendsSection(queryParams),
getTradeVolume(queryParams),
getStockIndexLimitUp(queryParams)
]) as any[]
getStockIndexLimitUp(queryParams),
])) as any[]
const trendTransformed = transformToColumnsAndRows(trendRes.data.rows)
trendColumns.value = trendTransformed.columns
trendData.value = trendTransformed.rows
@ -104,7 +164,9 @@ async function getList() {
volumeColumns.value = volumeTransformed.columns
volumeData.value = volumeTransformed.rows
const rawTransformed = transformToColumnsAndRows(volumeRes.data.rows?.map((item: any) => ({ ...item, sortDataList: item.rawDataList })) || [])
const rawTransformed = transformToColumnsAndRows(
volumeRes.data.rows?.map((item: any) => ({ ...item, sortDataList: item.rawDataList })) || []
)
volumeRawData.value = rawTransformed.rows
const limitTransformed = transformToColumnsAndRows(limitRes.data.rows)
@ -115,8 +177,13 @@ async function getList() {
}
}
function handleQuery() { getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleQuery() {
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleCellClick(row: any, column: any) {
console.log('Cell clicked:', row, column)
}
@ -127,7 +194,14 @@ onMounted(() => {
</script>
<style scoped>
.app-container { padding: 20px; }
.tab-header { margin-bottom: 10px; border-bottom: 2px solid #A9A4A4; }
.tab-title { font-weight: bold; }
.app-container {
padding: 20px;
}
.tab-header {
margin-bottom: 10px;
border-bottom: 2px solid #a9a4a4;
}
.tab-title {
font-weight: bold;
}
</style>

@ -1,15 +1,46 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="参数名称" prop="configName"><el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable size="small" style="width: 240px" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="参数键名" prop="configKey"><el-input v-model="queryParams.configKey" placeholder="请输入参数键名" clearable size="small" style="width: 240px" @keyup.enter="handleQuery" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="参数名称" prop="configName"
><el-input
v-model="queryParams.configName"
placeholder="请输入参数名称"
clearable
size="small"
style="width: 240px"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="参数键名" prop="configKey"
><el-input
v-model="queryParams.configKey"
placeholder="请输入参数键名"
clearable
size="small"
style="width: 240px"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="系统内置" prop="configType">
<el-select v-model="queryParams.configType" placeholder="系统内置" clearable size="small">
<el-option label="是" value="Y" /><el-option label="否" value="N" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="" end-placeholder="" />
<el-date-picker
v-model="dateRange"
size="small"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
@ -18,44 +49,133 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:config:add']"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['system:config:edit']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:config:remove']"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['system:config:export']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" @click="handleRefreshCache"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:config:add']"
>新增</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:config:edit']"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:config:remove']"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['system:config:export']"
>导出</el-button
></el-col
>
<el-col :span="1.5"
><el-button type="danger" plain size="small" @click="handleRefreshCache"
>刷新缓存</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column
label="参数名称"
align="center"
prop="configName"
:show-overflow-tooltip="true"
/>
<el-table-column
label="参数键名"
align="center"
prop="configKey"
:show-overflow-tooltip="true"
/>
<el-table-column label="参数键值" align="center" prop="configValue" />
<el-table-column label="系统内置" align="center" prop="configType">
<template #default="scope"><el-tag :type="scope.row.configType === 'Y' ? 'success' : 'warning'">{{ scope.row.configType === 'Y' ? '是' : '否' }}</el-tag></template>
<template #default="scope"
><el-tag :type="scope.row.configType === 'Y' ? 'success' : 'warning'">{{
scope.row.configType === 'Y' ? '是' : '否'
}}</el-tag></template
>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:config:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:config:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:config:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="参数名称" prop="configName"><el-input v-model="form.configName" placeholder="请输入参数名称" /></el-form-item>
<el-form-item label="参数键名" prop="configKey"><el-input v-model="form.configKey" placeholder="请输入参数键名" /></el-form-item>
<el-form-item label="参数键值" prop="configValue"><el-input v-model="form.configValue" placeholder="请输入参数键值" /></el-form-item>
<el-form-item label="参数名称" prop="configName"
><el-input v-model="form.configName" placeholder="请输入参数名称"
/></el-form-item>
<el-form-item label="参数键名" prop="configKey"
><el-input v-model="form.configKey" placeholder="请输入参数键名"
/></el-form-item>
<el-form-item label="参数键值" prop="configValue"
><el-input v-model="form.configValue" placeholder="请输入参数键值"
/></el-form-item>
<el-form-item label="系统内置" prop="configType">
<el-radio-group v-model="form.configType"><el-radio value="Y"></el-radio><el-radio value="N"></el-radio></el-radio-group>
<el-radio-group v-model="form.configType"
><el-radio value="Y"></el-radio><el-radio value="N"></el-radio></el-radio-group
>
</el-form-item>
<el-form-item label="备注" prop="remark"><el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /></el-form-item>
<el-form-item label="备注" prop="remark"
><el-input v-model="form.remark" type="textarea" placeholder="请输入内容"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -69,7 +189,14 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getConfigList, getConfig, delConfig, addConfig, updateConfig, refreshConfigCache } from '@/api/modules/system/config'
import {
getConfigList,
getConfig,
delConfig,
addConfig,
updateConfig,
refreshConfigCache,
} from '@/api/modules/system/config'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -84,39 +211,130 @@ const title = ref('')
const open = ref(false)
const dateRange = ref<string[]>([])
const queryParams = reactive({ pageNum: 1, pageSize: 10, configName: '', configKey: '', configType: '' })
const form = ref<any>({ configId: undefined, configName: '', configKey: '', configValue: '', configType: 'Y', remark: '' })
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
configName: '',
configKey: '',
configType: '',
})
const form = ref<any>({
configId: undefined,
configName: '',
configKey: '',
configValue: '',
configType: 'Y',
remark: '',
})
const rules = {
configName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],
configKey: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }],
configValue: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }]
configValue: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }],
}
async function getList() {
loading.value = true
try {
const params = { ...queryParams }
if (dateRange.value && dateRange.value.length === 2) { Object.assign(params, { beginTime: dateRange.value[0], endTime: dateRange.value[1] }) }
const res: any = await getConfigList(queryParams); configList.value = res.data.rows; total.value = res.data.total
} finally { loading.value = false }
if (dateRange.value && dateRange.value.length === 2) {
Object.assign(params, { beginTime: dateRange.value[0], endTime: dateRange.value[1] })
}
const res: any = await getConfigList(queryParams)
configList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { dateRange.value = []; queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.configId); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { configId: undefined, configName: '', configKey: '', configValue: '', configType: 'Y', remark: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加参数' }
async function handleUpdate(row?: any) { reset(); const configId = row?.configId || ids.value[0]; const res = await getConfig(configId); form.value = res.data; open.value = true; title.value = '修改参数' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.configId != undefined) { await updateConfig(form.value); ElMessage.success('修改成功') } else { await addConfig(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const configIds = row?.configId || ids.value; ElMessageBox.confirm(`是否确认删除参数编号为"${configIds}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delConfig(configIds); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/system/config/export`, '_blank') }
async function handleRefreshCache() { await refreshConfigCache(); ElMessage.success('刷新成功') }
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
dateRange.value = []
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.configId)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
configId: undefined,
configName: '',
configKey: '',
configValue: '',
configType: 'Y',
remark: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加参数'
}
async function handleUpdate(row?: any) {
reset()
const configId = row?.configId || ids.value[0]
const res = await getConfig(configId)
form.value = res.data
open.value = true
title.value = '修改参数'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.configId != undefined) {
await updateConfig(form.value)
ElMessage.success('修改成功')
} else {
await addConfig(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const configIds = row?.configId || ids.value
ElMessageBox.confirm(`是否确认删除参数编号为"${configIds}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delConfig(configIds)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/system/config/export`, '_blank')
}
async function handleRefreshCache() {
await refreshConfigCache()
ElMessage.success('刷新成功')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -2,7 +2,13 @@
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch">
<el-form-item label="部门名称" prop="deptName">
<el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable size="small" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.deptName"
placeholder="请输入部门名称"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="部门状态" clearable size="small">
@ -18,7 +24,14 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:dept:add']"></el-button>
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:dept:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain size="small" @click="toggleExpandAll">/</el-button>
@ -46,9 +59,31 @@
<el-table-column label="创建时间" align="center" prop="createTime" width="200" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']"></el-button>
<el-button v-if="scope.row.parentId != 0" size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:dept:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleAdd(scope.row)"
v-hasPermi="['system:dept:add']"
>新增</el-button
>
<el-button
v-if="scope.row.parentId != 0"
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:dept:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
@ -115,7 +150,14 @@
import { ref, reactive, onMounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getDeptList, getDept, delDept, addDept, updateDept, getDeptListExcludeChild } from '@/api/modules/system/dept'
import {
getDeptList,
getDept,
delDept,
addDept,
updateDept,
getDeptListExcludeChild,
} from '@/api/modules/system/dept'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -139,7 +181,7 @@ const form = ref<any>({
leader: '',
phone: '',
email: '',
status: '0'
status: '0',
})
const rules = {
@ -147,11 +189,17 @@ const rules = {
deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '显示排序不能为空', trigger: 'blur' }],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }]
phone: [
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' },
],
}
function handleTree(data: any[], id: string, parentId?: string, children?: string) {
const config = { id: id || 'id', parentId: parentId || 'parentId', childrenList: children || 'children' }
const config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children',
}
const childrenListMap: any = {}
const nodeIds: any = {}
const tree: any[] = []
@ -189,13 +237,20 @@ async function getList() {
}
}
function handleQuery() { getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleQuery() {
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function toggleExpandAll() {
refreshTable.value = false
isExpandAll.value = !isExpandAll.value
nextTick(() => { refreshTable.value = true })
nextTick(() => {
refreshTable.value = true
})
}
async function handleAdd(row?: any) {
@ -234,24 +289,44 @@ async function submitForm() {
function handleDelete(row: any) {
ElMessageBox.confirm(`是否确认删除名称为"${row.deptName}"的数据项?`, '系统提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
}).then(async () => {
await delDept(row.deptId)
ElMessage.success('删除成功')
getList()
}).catch(() => {})
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delDept(row.deptId)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function reset() {
form.value = { deptId: undefined, parentId: undefined, deptName: '', orderNum: undefined, leader: '', phone: '', email: '', status: '0' }
form.value = {
deptId: undefined,
parentId: undefined,
deptName: '',
orderNum: undefined,
leader: '',
phone: '',
email: '',
status: '0',
}
formRef.value?.resetFields()
}
function cancel() { open.value = false; reset() }
function cancel() {
open.value = false
reset()
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.app-container {
padding: 20px;
}
</style>

@ -1,13 +1,30 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="字典名称" prop="dictType">
<el-select v-model="queryParams.dictType" size="small">
<el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" />
<el-option
v-for="item in typeOptions"
:key="item.dictId"
:label="item.dictName"
:value="item.dictType"
/>
</el-select>
</el-form-item>
<el-form-item label="字典标签" prop="dictLabel">
<el-input v-model="queryParams.dictLabel" placeholder="请输入字典标签" clearable size="small" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.dictLabel"
placeholder="请输入字典标签"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="数据状态" clearable size="small">
@ -23,16 +40,46 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:dict:add']"></el-button>
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:dict:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['system:dict:edit']"></el-button>
<el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:dict:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:dict:remove']"></el-button>
<el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:dict:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['system:dict:export']"></el-button>
<el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['system:dict:export']"
>导出</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain size="small" @click="handleClose"></el-button>
@ -48,28 +95,62 @@
<el-table-column label="字典排序" align="center" prop="dictSort" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{ scope.row.status === '0' ? '正常' : '停用' }}</el-tag>
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{
scope.row.status === '0' ? '正常' : '停用'
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:dict:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:dict:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" :page-sizes="[10, 20, 30, 50]" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 30, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="字典类型"><el-input v-model="form.dictType" :disabled="true" /></el-form-item>
<el-form-item label="数据标签" prop="dictLabel"><el-input v-model="form.dictLabel" placeholder="请输入数据标签" /></el-form-item>
<el-form-item label="数据键值" prop="dictValue"><el-input v-model="form.dictValue" placeholder="请输入数据键值" /></el-form-item>
<el-form-item label="显示排序" prop="dictSort"><el-input-number v-model="form.dictSort" controls-position="right" :min="0" /></el-form-item>
<el-form-item label="字典类型"
><el-input v-model="form.dictType" :disabled="true"
/></el-form-item>
<el-form-item label="数据标签" prop="dictLabel"
><el-input v-model="form.dictLabel" placeholder="请输入数据标签"
/></el-form-item>
<el-form-item label="数据键值" prop="dictValue"
><el-input v-model="form.dictValue" placeholder="请输入数据键值"
/></el-form-item>
<el-form-item label="显示排序" prop="dictSort"
><el-input-number v-model="form.dictSort" controls-position="right" :min="0"
/></el-form-item>
<el-form-item label="回显样式" prop="listClass">
<el-select v-model="form.listClass">
<el-option label="默认" value="default" /><el-option label="主要" value="primary" />
@ -78,9 +159,13 @@
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status"><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group>
<el-radio-group v-model="form.status"
><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group
>
</el-form-item>
<el-form-item label="备注" prop="remark"><el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /></el-form-item>
<el-form-item label="备注" prop="remark"
><el-input v-model="form.remark" type="textarea" placeholder="请输入内容"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -94,7 +179,13 @@
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getDictDataList, getDictData, delDictData, addDictData, updateDictData } from '@/api/modules/system/dict'
import {
getDictDataList,
getDictData,
delDictData,
addDictData,
updateDictData,
} from '@/api/modules/system/dict'
import { getDictType, getDictTypeList } from '@/api/modules/system/dict'
const { proxy } = getCurrentInstance() as any
@ -115,12 +206,20 @@ const typeOptions = ref<any[]>([])
const queryParams = reactive({ pageNum: 1, pageSize: 10, dictType: '', dictLabel: '', status: '' })
const form = ref<any>({ dictCode: undefined, dictLabel: '', dictValue: '', dictSort: 0, listClass: 'default', status: '0', remark: '' })
const form = ref<any>({
dictCode: undefined,
dictLabel: '',
dictValue: '',
dictSort: 0,
listClass: 'default',
status: '0',
remark: '',
})
const rules = {
dictLabel: [{ required: true, message: '数据标签不能为空', trigger: 'blur' }],
dictValue: [{ required: true, message: '数据键值不能为空', trigger: 'blur' }],
dictSort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }]
dictSort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }],
}
async function getType(dictId: string) {
@ -141,22 +240,50 @@ async function getList() {
const res: any = await getDictDataList(queryParams)
dataList.value = res.data.rows
total.value = res.data.total
} finally { loading.value = false }
} finally {
loading.value = false
}
}
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); queryParams.dictType = defaultDictType.value; handleQuery() }
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
queryParams.dictType = defaultDictType.value
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map(item => item.dictCode)
ids.value = selection.map((item) => item.dictCode)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() { form.value = { dictCode: undefined, dictLabel: '', dictValue: '', dictSort: 0, listClass: 'default', status: '0', remark: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function reset() {
form.value = {
dictCode: undefined,
dictLabel: '',
dictValue: '',
dictSort: 0,
listClass: 'default',
status: '0',
remark: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() { reset(); open.value = true; title.value = '添加字典数据'; form.value.dictType = queryParams.dictType }
function handleAdd() {
reset()
open.value = true
title.value = '添加字典数据'
form.value.dictType = queryParams.dictType
}
async function handleUpdate(row?: any) {
reset()
@ -170,19 +297,36 @@ async function handleUpdate(row?: any) {
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.dictCode != undefined) { await updateDictData(form.value); ElMessage.success('修改成功') }
else { await addDictData(form.value); ElMessage.success('新增成功') }
open.value = false; getList()
if (form.value.dictCode != undefined) {
await updateDictData(form.value)
ElMessage.success('修改成功')
} else {
await addDictData(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const dictCodes = row?.dictCode || ids.value
ElMessageBox.confirm(`是否确认删除字典编码为"${dictCodes}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(async () => { await delDictData(dictCodes); ElMessage.success('删除成功'); getList() }).catch(() => {})
ElMessageBox.confirm(`是否确认删除字典编码为"${dictCodes}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delDictData(dictCodes)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/system/dict/data/export`, '_blank') }
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/system/dict/data/export`, '_blank')
}
function handleClose() {
const obj = { path: '/system/dict' }
@ -197,6 +341,10 @@ onMounted(() => {
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,20 +1,55 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="字典名称" prop="dictName">
<el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable size="small" style="width: 240px" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.dictName"
placeholder="请输入字典名称"
clearable
size="small"
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="字典类型" prop="dictType">
<el-input v-model="queryParams.dictType" placeholder="请输入字典类型" clearable size="small" style="width: 240px" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.dictType"
placeholder="请输入字典类型"
clearable
size="small"
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="字典状态" clearable size="small" style="width: 240px">
<el-select
v-model="queryParams.status"
placeholder="字典状态"
clearable
size="small"
style="width: 240px"
>
<el-option label="正常" value="0" />
<el-option label="停用" value="1" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="" end-placeholder="" />
<el-date-picker
v-model="dateRange"
size="small"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleQuery"></el-button>
@ -24,16 +59,46 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:dict:add']"></el-button>
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:dict:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['system:dict:edit']"></el-button>
<el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:dict:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:dict:remove']"></el-button>
<el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:dict:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['system:dict:export']"></el-button>
<el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['system:dict:export']"
>导出</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain size="small" @click="handleRefreshCache"></el-button>
@ -44,7 +109,12 @@
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典编号" align="center" prop="dictId" />
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
<el-table-column
label="字典名称"
align="center"
prop="dictName"
:show-overflow-tooltip="true"
/>
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<router-link :to="`/system/dict-data/index/${scope.row.dictId}`" class="link-type">
@ -54,30 +124,64 @@
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{ scope.row.status === '0' ? '正常' : '停用' }}</el-tag>
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{
scope.row.status === '0' ? '正常' : '停用'
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:dict:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:dict:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" :page-sizes="[10, 20, 30, 50]" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 30, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="字典名称" prop="dictName"><el-input v-model="form.dictName" placeholder="请输入字典名称" /></el-form-item>
<el-form-item label="字典类型" prop="dictType"><el-input v-model="form.dictType" placeholder="请输入字典类型" /></el-form-item>
<el-form-item label="字典名称" prop="dictName"
><el-input v-model="form.dictName" placeholder="请输入字典名称"
/></el-form-item>
<el-form-item label="字典类型" prop="dictType"
><el-input v-model="form.dictType" placeholder="请输入字典类型"
/></el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status"><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group>
<el-radio-group v-model="form.status"
><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group
>
</el-form-item>
<el-form-item label="备注" prop="remark"><el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /></el-form-item>
<el-form-item label="备注" prop="remark"
><el-input v-model="form.remark" type="textarea" placeholder="请输入内容"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -91,7 +195,14 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getDictTypeList, getDictType, delDictType, addDictType, updateDictType, refreshDictCache } from '@/api/modules/system/dict'
import {
getDictTypeList,
getDictType,
delDictType,
addDictType,
updateDictType,
refreshDictCache,
} from '@/api/modules/system/dict'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -113,7 +224,7 @@ const form = ref<any>({ dictId: undefined, dictName: '', dictType: '', status: '
const rules = {
dictName: [{ required: true, message: '字典名称不能为空', trigger: 'blur' }],
dictType: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }]
dictType: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }],
}
async function getList() {
@ -126,22 +237,41 @@ async function getList() {
const res: any = await getDictTypeList(params)
typeList.value = res.data.rows
total.value = res.data.total
} finally { loading.value = false }
} finally {
loading.value = false
}
}
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { dateRange.value = []; queryFormRef.value?.resetFields(); handleQuery() }
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
dateRange.value = []
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map(item => item.dictId)
ids.value = selection.map((item) => item.dictId)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() { form.value = { dictId: undefined, dictName: '', dictType: '', status: '0', remark: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function reset() {
form.value = { dictId: undefined, dictName: '', dictType: '', status: '0', remark: '' }
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() { reset(); open.value = true; title.value = '添加字典类型' }
function handleAdd() {
reset()
open.value = true
title.value = '添加字典类型'
}
async function handleUpdate(row?: any) {
reset()
@ -155,30 +285,56 @@ async function handleUpdate(row?: any) {
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.dictId != undefined) { await updateDictType(form.value); ElMessage.success('修改成功') }
else { await addDictType(form.value); ElMessage.success('新增成功') }
open.value = false; getList()
if (form.value.dictId != undefined) {
await updateDictType(form.value)
ElMessage.success('修改成功')
} else {
await addDictType(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const dictIds = row?.dictId || ids.value
ElMessageBox.confirm(`是否确认删除字典编号为"${dictIds}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(async () => { await delDictType(dictIds); ElMessage.success('删除成功'); getList() }).catch(() => {})
ElMessageBox.confirm(`是否确认删除字典编号为"${dictIds}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delDictType(dictIds)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/system/dict/type/export`, '_blank') }
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/system/dict/type/export`, '_blank')
}
async function handleRefreshCache() {
await refreshDictCache()
ElMessage.success('刷新成功')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.link-type { color: #409eff; cursor: pointer; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
.link-type {
color: #409eff;
cursor: pointer;
}
</style>

@ -24,7 +24,14 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:menu:add']"></el-button>
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:menu:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain size="small" @click="toggleExpandAll">/</el-button>
@ -59,9 +66,30 @@
<el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:menu:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleAdd(scope.row)"
v-hasPermi="['system:menu:add']"
>新增</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:menu:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
@ -192,7 +220,7 @@ const refreshTable = ref(true)
const queryParams = reactive({
menuName: '',
status: ''
status: '',
})
const form = ref<any>({
@ -208,13 +236,13 @@ const form = ref<any>({
status: '0',
perms: '',
path: '',
component: ''
component: '',
})
const rules = {
menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
}
async function getList() {
@ -228,7 +256,11 @@ async function getList() {
}
function handleTree(data: any[], id: string, parentId?: string, children?: string) {
const config = { id: id || 'id', parentId: parentId || 'parentId', childrenList: children || 'children' }
const config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children',
}
const childrenListMap: any = {}
const nodeIds: any = {}
const tree: any[] = []
@ -323,12 +355,14 @@ function handleDelete(row: any) {
ElMessageBox.confirm(`是否确认删除名称为"${row.menuName}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await delMenu(row.menuId)
ElMessage.success('删除成功')
getList()
}).catch(() => {})
type: 'warning',
})
.then(async () => {
await delMenu(row.menuId)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function reset() {
@ -345,7 +379,7 @@ function reset() {
status: '0',
perms: '',
path: '',
component: ''
component: '',
}
formRef.value?.resetFields()
}
@ -361,5 +395,7 @@ onMounted(() => {
</script>
<style scoped>
.app-container { padding: 20px; }
.app-container {
padding: 20px;
}
</style>

@ -1,11 +1,29 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable size="small" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.noticeTitle"
placeholder="请输入公告标题"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="操作人员" prop="createBy">
<el-input v-model="queryParams.createBy" placeholder="请输入操作人员" clearable size="small" @keyup.enter="handleQuery" />
<el-input
v-model="queryParams.createBy"
placeholder="请输入操作人员"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="类型" prop="noticeType">
<el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable size="small">
@ -20,13 +38,36 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:notice:add']"></el-button>
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:notice:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['system:notice:edit']"></el-button>
<el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:notice:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:notice:remove']"></el-button>
<el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:notice:remove']"
>删除</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -34,34 +75,68 @@
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="noticeId" width="100" />
<el-table-column label="公告标题" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />
<el-table-column
label="公告标题"
align="center"
prop="noticeTitle"
:show-overflow-tooltip="true"
/>
<el-table-column label="公告类型" align="center" prop="noticeType" width="100">
<template #default="scope">
<el-tag :type="scope.row.noticeType === '1' ? 'primary' : 'success'">{{ scope.row.noticeType === '1' ? '通知' : '公告' }}</el-tag>
<el-tag :type="scope.row.noticeType === '1' ? 'primary' : 'success'">{{
scope.row.noticeType === '1' ? '通知' : '公告'
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{ scope.row.status === '0' ? '正常' : '关闭' }}</el-tag>
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{
scope.row.status === '0' ? '正常' : '关闭'
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="创建者" align="center" prop="createBy" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="100" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:notice:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:notice:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:notice:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:notice:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="780px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="公告标题" prop="noticeTitle"><el-input v-model="form.noticeTitle" placeholder="请输入公告标题" /></el-form-item>
<el-form-item label="公告标题" prop="noticeTitle"
><el-input v-model="form.noticeTitle" placeholder="请输入公告标题"
/></el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公告类型" prop="noticeType">
@ -72,11 +147,20 @@
</el-col>
<el-col :span="24">
<el-form-item label="状态">
<el-radio-group v-model="form.status"><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group>
<el-radio-group v-model="form.status"
><el-radio value="0">正常</el-radio
><el-radio value="1">关闭</el-radio></el-radio-group
>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容"><el-input v-model="form.noticeContent" type="textarea" :autosize="{ minRows: 6, maxRows: 12 }" placeholder="请输入内容" /></el-form-item>
<el-form-item label="内容"
><el-input
v-model="form.noticeContent"
type="textarea"
:autosize="{ minRows: 6, maxRows: 12 }"
placeholder="请输入内容"
/></el-form-item>
</el-col>
</el-row>
</el-form>
@ -92,7 +176,13 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance } from 'element-plus'
import { getNoticeList, getNotice, delNotice, addNotice, updateNotice } from '@/api/modules/system/notice'
import {
getNoticeList,
getNotice,
delNotice,
addNotice,
updateNotice,
} from '@/api/modules/system/notice'
const queryFormRef = ref<FormInstance>()
const formRef = ref<FormInstance>()
@ -107,28 +197,114 @@ const ids = ref<any[]>([])
const title = ref('')
const open = ref(false)
const queryParams = reactive({ pageNum: 1, pageSize: 10, noticeTitle: '', createBy: '', noticeType: '' })
const form = ref<any>({ noticeId: undefined, noticeTitle: '', noticeType: '', noticeContent: '', status: '0' })
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
noticeTitle: '',
createBy: '',
noticeType: '',
})
const form = ref<any>({
noticeId: undefined,
noticeTitle: '',
noticeType: '',
noticeContent: '',
status: '0',
})
const rules = {
noticeTitle: [{ required: true, message: '公告标题不能为空', trigger: 'blur' }],
noticeType: [{ required: true, message: '公告类型不能为空', trigger: 'change' }]
noticeType: [{ required: true, message: '公告类型不能为空', trigger: 'change' }],
}
async function getList() { loading.value = true; try { const res: any = await getNoticeList(queryParams); noticeList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.noticeId); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { noticeId: undefined, noticeTitle: '', noticeType: '', noticeContent: '', status: '0' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加公告' }
async function handleUpdate(row?: any) { reset(); const noticeId = row?.noticeId || ids.value[0]; const res = await getNotice(noticeId); form.value = res.data; open.value = true; title.value = '修改公告' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.noticeId != undefined) { await updateNotice(form.value); ElMessage.success('修改成功') } else { await addNotice(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const noticeIds = row?.noticeId || ids.value; ElMessageBox.confirm(`是否确认删除公告编号为"${noticeIds}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delNotice(noticeIds); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
async function getList() {
loading.value = true
try {
const res: any = await getNoticeList(queryParams)
noticeList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.noticeId)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
noticeId: undefined,
noticeTitle: '',
noticeType: '',
noticeContent: '',
status: '0',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加公告'
}
async function handleUpdate(row?: any) {
reset()
const noticeId = row?.noticeId || ids.value[0]
const res = await getNotice(noticeId)
form.value = res.data
open.value = true
title.value = '修改公告'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.noticeId != undefined) {
await updateNotice(form.value)
ElMessage.success('修改成功')
} else {
await addNotice(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const noticeIds = row?.noticeId || ids.value
ElMessageBox.confirm(`是否确认删除公告编号为"${noticeIds}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delNotice(noticeIds)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -1,8 +1,28 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="岗位编码" prop="postCode"><el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form-item label="岗位名称" prop="postName"><el-input v-model="queryParams.postName" placeholder="请输入岗位名称" clearable size="small" @keyup.enter="handleQuery" /></el-form-item>
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="岗位编码" prop="postCode"
><el-input
v-model="queryParams.postCode"
placeholder="请输入岗位编码"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="岗位名称" prop="postName"
><el-input
v-model="queryParams.postName"
placeholder="请输入岗位名称"
clearable
size="small"
@keyup.enter="handleQuery"
/></el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="岗位状态" clearable size="small">
<el-option label="正常" value="0" /><el-option label="停用" value="1" />
@ -15,10 +35,48 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"><el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:post:add']"></el-button></el-col>
<el-col :span="1.5"><el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['system:post:edit']"></el-button></el-col>
<el-col :span="1.5"><el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:post:remove']"></el-button></el-col>
<el-col :span="1.5"><el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['system:post:export']"></el-button></el-col>
<el-col :span="1.5"
><el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:post:add']"
>新增</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:post:edit']"
>修改</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:post:remove']"
>删除</el-button
></el-col
>
<el-col :span="1.5"
><el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['system:post:export']"
>导出</el-button
></el-col
>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -29,28 +87,65 @@
<el-table-column label="岗位名称" align="center" prop="postName" />
<el-table-column label="岗位排序" align="center" prop="postSort" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope"><el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{ scope.row.status === '0' ? '正常' : '停用' }}</el-tag></template>
<template #default="scope"
><el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">{{
scope.row.status === '0' ? '正常' : '停用'
}}</el-tag></template
>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:post:remove']"></el-button>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:post:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:post:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @current-change="getList" @size-change="getList" class="mt-4" />
<el-pagination
v-show="total > 0"
:total="total"
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
@current-change="getList"
@size-change="getList"
class="mt-4"
/>
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="岗位名称" prop="postName"><el-input v-model="form.postName" placeholder="请输入岗位名称" /></el-form-item>
<el-form-item label="岗位编码" prop="postCode"><el-input v-model="form.postCode" placeholder="请输入编码名称" /></el-form-item>
<el-form-item label="岗位顺序" prop="postSort"><el-input-number v-model="form.postSort" controls-position="right" :min="0" /></el-form-item>
<el-form-item label="岗位名称" prop="postName"
><el-input v-model="form.postName" placeholder="请输入岗位名称"
/></el-form-item>
<el-form-item label="岗位编码" prop="postCode"
><el-input v-model="form.postCode" placeholder="请输入编码名称"
/></el-form-item>
<el-form-item label="岗位顺序" prop="postSort"
><el-input-number v-model="form.postSort" controls-position="right" :min="0"
/></el-form-item>
<el-form-item label="岗位状态" prop="status">
<el-radio-group v-model="form.status"><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group>
<el-radio-group v-model="form.status"
><el-radio value="0">正常</el-radio><el-radio value="1"></el-radio></el-radio-group
>
</el-form-item>
<el-form-item label="备注" prop="remark"><el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /></el-form-item>
<el-form-item label="备注" prop="remark"
><el-input v-model="form.remark" type="textarea" placeholder="请输入内容"
/></el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
@ -79,29 +174,113 @@ const title = ref('')
const open = ref(false)
const queryParams = reactive({ pageNum: 1, pageSize: 10, postCode: '', postName: '', status: '' })
const form = ref<any>({ postId: undefined, postCode: '', postName: '', postSort: 0, status: '0', remark: '' })
const form = ref<any>({
postId: undefined,
postCode: '',
postName: '',
postSort: 0,
status: '0',
remark: '',
})
const rules = {
postName: [{ required: true, message: '岗位名称不能为空', trigger: 'blur' }],
postCode: [{ required: true, message: '岗位编码不能为空', trigger: 'blur' }],
postSort: [{ required: true, message: '岗位顺序不能为空', trigger: 'blur' }]
postSort: [{ required: true, message: '岗位顺序不能为空', trigger: 'blur' }],
}
async function getList() { loading.value = true; try { const res: any = await getPostList(queryParams); postList.value = res.data.rows; total.value = res.data.total } finally { loading.value = false } }
function handleQuery() { queryParams.pageNum = 1; getList() }
function resetQuery() { queryFormRef.value?.resetFields(); handleQuery() }
function handleSelectionChange(selection: any[]) { ids.value = selection.map(item => item.postId); single.value = selection.length !== 1; multiple.value = !selection.length }
function reset() { form.value = { postId: undefined, postCode: '', postName: '', postSort: 0, status: '0', remark: '' }; formRef.value?.resetFields() }
function cancel() { open.value = false; reset() }
function handleAdd() { reset(); open.value = true; title.value = '添加岗位' }
async function handleUpdate(row?: any) { reset(); const postId = row?.postId || ids.value[0]; const res = await getPost(postId); form.value = res.data; open.value = true; title.value = '修改岗位' }
async function submitForm() { const valid = await formRef.value?.validate().catch(() => false); if (valid) { if (form.value.postId != undefined) { await updatePost(form.value); ElMessage.success('修改成功') } else { await addPost(form.value); ElMessage.success('新增成功') } open.value = false; getList() } }
function handleDelete(row?: any) { const postIds = row?.postId || ids.value; ElMessageBox.confirm(`是否确认删除岗位编号为"${postIds}"的数据项?`, '系统提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await delPost(postIds); ElMessage.success('删除成功'); getList() }).catch(() => {}) }
function handleExport() { window.open(`${import.meta.env.VITE_APP_BASE_API}/system/post/export`, '_blank') }
async function getList() {
loading.value = true
try {
const res: any = await getPostList(queryParams)
postList.value = res.data.rows
total.value = res.data.total
} finally {
loading.value = false
}
}
function handleQuery() {
queryParams.pageNum = 1
getList()
}
function resetQuery() {
queryFormRef.value?.resetFields()
handleQuery()
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map((item) => item.postId)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function reset() {
form.value = {
postId: undefined,
postCode: '',
postName: '',
postSort: 0,
status: '0',
remark: '',
}
formRef.value?.resetFields()
}
function cancel() {
open.value = false
reset()
}
function handleAdd() {
reset()
open.value = true
title.value = '添加岗位'
}
async function handleUpdate(row?: any) {
reset()
const postId = row?.postId || ids.value[0]
const res = await getPost(postId)
form.value = res.data
open.value = true
title.value = '修改岗位'
}
async function submitForm() {
const valid = await formRef.value?.validate().catch(() => false)
if (valid) {
if (form.value.postId != undefined) {
await updatePost(form.value)
ElMessage.success('修改成功')
} else {
await addPost(form.value)
ElMessage.success('新增成功')
}
open.value = false
getList()
}
}
function handleDelete(row?: any) {
const postIds = row?.postId || ids.value
ElMessageBox.confirm(`是否确认删除岗位编号为"${postIds}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await delPost(postIds)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
window.open(`${import.meta.env.VITE_APP_BASE_API}/system/post/export`, '_blank')
}
onMounted(() => { getList() })
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
</style>

@ -53,16 +53,46 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain size="small" @click="handleAdd" v-hasPermi="['system:role:add']"></el-button>
<el-button
type="primary"
plain
size="small"
@click="handleAdd"
v-hasPermi="['system:role:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain size="small" :disabled="single" @click="handleUpdate" v-hasPermi="['system:role:edit']"></el-button>
<el-button
type="success"
plain
size="small"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:role:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain size="small" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:role:remove']"></el-button>
<el-button
type="danger"
plain
size="small"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:role:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain size="small" @click="handleExport" v-hasPermi="['system:role:export']"></el-button>
<el-button
type="warning"
plain
size="small"
@click="handleExport"
v-hasPermi="['system:role:export']"
>导出</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@ -87,17 +117,41 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<template v-if="scope.row.roleId !== 1">
<el-button size="small" type="primary" link @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
<el-button size="small" type="primary" link @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
<el-dropdown size="small" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:role:edit']">
<span class="el-dropdown-link">更多<el-icon class="el-icon--right"><arrow-down /></el-icon></span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="handleDataScope" v-hasPermi="['system:role:edit']"></el-dropdown-item>
<el-dropdown-item command="handleAuthUser" v-hasPermi="['system:role:edit']"></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:role:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:role:remove']"
>删除</el-button
>
<el-dropdown
size="small"
@command="(command) => handleCommand(command, scope.row)"
v-hasPermi="['system:role:edit']"
>
<span class="el-dropdown-link"
>更多<el-icon class="el-icon--right"><arrow-down /></el-icon
></span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="handleDataScope" v-hasPermi="['system:role:edit']"
>数据权限</el-dropdown-item
>
<el-dropdown-item command="handleAuthUser" v-hasPermi="['system:role:edit']"
>分配用户</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</template>
</el-table-column>
@ -140,9 +194,21 @@
</el-radio-group>
</el-form-item>
<el-form-item label="菜单权限">
<el-checkbox v-model="menuExpand" @change="(val: any) => handleCheckedTreeExpand(val, 'menu')">/</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="(val: any) => handleCheckedTreeNodeAll(val, 'menu')">/</el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" @change="(val: any) => handleCheckedTreeConnect(val, 'menu')"></el-checkbox>
<el-checkbox
v-model="menuExpand"
@change="(val: any) => handleCheckedTreeExpand(val, 'menu')"
>展开/折叠</el-checkbox
>
<el-checkbox
v-model="menuNodeAll"
@change="(val: any) => handleCheckedTreeNodeAll(val, 'menu')"
>全选/全不选</el-checkbox
>
<el-checkbox
v-model="form.menuCheckStrictly"
@change="(val: any) => handleCheckedTreeConnect(val, 'menu')"
>父子联动</el-checkbox
>
<el-tree
class="tree-border"
:data="menuOptions"
@ -183,9 +249,21 @@
</el-select>
</el-form-item>
<el-form-item label="数据权限" v-show="form.dataScope == '2'">
<el-checkbox v-model="deptExpand" @change="(val: any) => handleCheckedTreeExpand(val, 'dept')">/</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="(val: any) => handleCheckedTreeNodeAll(val, 'dept')">/</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="(val: any) => handleCheckedTreeConnect(val, 'dept')"></el-checkbox>
<el-checkbox
v-model="deptExpand"
@change="(val: any) => handleCheckedTreeExpand(val, 'dept')"
>展开/折叠</el-checkbox
>
<el-checkbox
v-model="deptNodeAll"
@change="(val: any) => handleCheckedTreeNodeAll(val, 'dept')"
>全选/全不选</el-checkbox
>
<el-checkbox
v-model="form.deptCheckStrictly"
@change="(val: any) => handleCheckedTreeConnect(val, 'dept')"
>父子联动</el-checkbox
>
<el-tree
class="tree-border"
:data="deptOptions"
@ -222,7 +300,7 @@ import {
changeRoleStatus,
getMenuTreeselect,
roleMenuTreeselect,
roleDeptTreeselect
roleDeptTreeselect,
} from '@/api/modules/system/role'
const { proxy } = getCurrentInstance() as any
@ -255,7 +333,7 @@ const queryParams = reactive({
pageSize: 10,
roleName: '',
roleKey: '',
status: ''
status: '',
})
const form = ref<any>({
@ -268,13 +346,13 @@ const form = ref<any>({
deptIds: [],
menuCheckStrictly: true,
deptCheckStrictly: true,
remark: ''
remark: '',
})
const rules = {
roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
roleKey: [{ required: true, message: '权限字符不能为空', trigger: 'blur' }],
roleSort: [{ required: true, message: '角色顺序不能为空', trigger: 'blur' }]
roleSort: [{ required: true, message: '角色顺序不能为空', trigger: 'blur' }],
}
async function getList() {
@ -304,7 +382,7 @@ function resetQuery() {
}
function handleSelectionChange(selection: any[]) {
ids.value = selection.map(item => item.roleId)
ids.value = selection.map((item) => item.roleId)
single.value = selection.length !== 1
multiple.value = !selection.length
}
@ -314,13 +392,15 @@ function handleStatusChange(row: any) {
ElMessageBox.confirm(`确认要"${text}""${row.roleName}"角色吗?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await changeRoleStatus(row.roleId, row.status)
ElMessage.success(text + '成功')
}).catch(() => {
row.status = row.status === '0' ? '1' : '0'
type: 'warning',
})
.then(async () => {
await changeRoleStatus(row.roleId, row.status)
ElMessage.success(text + '成功')
})
.catch(() => {
row.status = row.status === '0' ? '1' : '0'
})
}
function handleCommand(command: string, row: any) {
@ -394,7 +474,7 @@ function reset() {
deptIds: [],
menuCheckStrictly: true,
deptCheckStrictly: true,
remark: ''
remark: '',
}
formRef.value?.resetFields()
}
@ -443,7 +523,7 @@ async function submitForm() {
function handleDataScope(row: any) {
reset()
const roleDept = roleDeptTreeselect(row.roleId)
getRole(row.roleId).then(response => {
getRole(row.roleId).then((response) => {
form.value = response.data
openDataScope.value = true
nextTick(() => {
@ -490,12 +570,14 @@ function handleDelete(row?: any) {
ElMessageBox.confirm(`是否确认删除角色编号为"${roleIds}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await delRole(roleIds)
ElMessage.success('删除成功')
getList()
}).catch(() => {})
type: 'warning',
})
.then(async () => {
await delRole(roleIds)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
function handleExport() {
@ -508,7 +590,16 @@ onMounted(() => {
</script>
<style scoped>
.app-container { padding: 20px; }
.mt-4 { margin-top: 16px; }
.tree-border { margin-top: 8px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 8px; }
.app-container {
padding: 20px;
}
.mt-4 {
margin-top: 16px;
}
.tree-border {
margin-top: 8px;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 8px;
}
</style>

@ -27,7 +27,13 @@
</el-col>
<!-- 用户数据 -->
<el-col :span="20" :xs="24">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form
:model="queryParams"
ref="queryFormRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="queryParams.userName"
@ -86,7 +92,8 @@
size="small"
@click="handleAdd"
v-hasPermi="['system:user:add']"
>新增</el-button>
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
@ -96,7 +103,8 @@
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:user:edit']"
>修改</el-button>
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
@ -106,7 +114,8 @@
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:user:remove']"
>删除</el-button>
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
@ -115,7 +124,8 @@
size="small"
@click="handleImport"
v-hasPermi="['system:user:import']"
>导入</el-button>
>导入</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
@ -124,18 +134,53 @@
size="small"
@click="handleExport"
v-hasPermi="['system:user:export']"
>导出</el-button>
>导出</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns" />
</el-row>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
<el-table-column
label="用户编号"
align="center"
key="userId"
prop="userId"
v-if="columns[0].visible"
/>
<el-table-column
label="用户名称"
align="center"
key="userName"
prop="userName"
v-if="columns[1].visible"
:show-overflow-tooltip="true"
/>
<el-table-column
label="用户昵称"
align="center"
key="nickName"
prop="nickName"
v-if="columns[2].visible"
:show-overflow-tooltip="true"
/>
<el-table-column
label="部门"
align="center"
key="deptName"
prop="dept.deptName"
v-if="columns[3].visible"
:show-overflow-tooltip="true"
/>
<el-table-column
label="手机号码"
align="center"
key="phonenumber"
prop="phonenumber"
v-if="columns[4].visible"
width="120"
/>
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
<template #default="scope">
<el-switch
@ -146,35 +191,58 @@
/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160" />
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<el-table-column
label="创建时间"
align="center"
prop="createTime"
v-if="columns[6].visible"
width="160"
/>
<el-table-column
label="操作"
align="center"
width="160"
class-name="small-padding fixed-width"
>
<template #default="scope">
<template v-if="scope.row.userId !== 1">
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:user:edit']"
>修改</el-button>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:user:remove']"
>删除</el-button>
<el-dropdown size="small" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
<span class="el-dropdown-link">
更多<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="handleResetPwd" v-hasPermi="['system:user:resetPwd']"></el-dropdown-item>
<el-dropdown-item command="handleAuthRole" v-hasPermi="['system:user:edit']"></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button
size="small"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:user:edit']"
>修改</el-button
>
<el-button
size="small"
type="primary"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:user:remove']"
>删除</el-button
>
<el-dropdown
size="small"
@command="(command) => handleCommand(command, scope.row)"
v-hasPermi="['system:user:resetPwd', 'system:user:edit']"
>
<span class="el-dropdown-link">
更多<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
command="handleResetPwd"
v-hasPermi="['system:user:resetPwd']"
>重置密码</el-dropdown-item
>
<el-dropdown-item command="handleAuthRole" v-hasPermi="['system:user:edit']"
>分配角色</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</template>
</el-table-column>
@ -235,7 +303,13 @@
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
<el-input
v-model="form.password"
placeholder="请输入用户密码"
type="password"
maxlength="20"
show-password
/>
</el-form-item>
</el-col>
</el-row>
@ -346,7 +420,7 @@ import {
resetUserPwd,
changeUserStatus,
getDeptTreeselect,
getConfigKey
getConfigKey,
} from '@/api/modules/system/user'
import { getToken } from '@/utils/auth'
import type { ColumnOption } from '@/types'
@ -379,7 +453,7 @@ const queryParams = reactive({
userName: '',
phonenumber: '',
status: '',
deptId: undefined as any
deptId: undefined as any,
})
const form = ref<any>({
@ -394,27 +468,23 @@ const form = ref<any>({
status: '0',
remark: '',
postIds: [],
roleIds: []
roleIds: [],
})
const rules = {
userName: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
nickName: [
{ required: true, message: '用户昵称不能为空', trigger: 'blur' }
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' },
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
],
email: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phonenumber: [
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' },
],
}
const columns = ref<ColumnOption[]>([
@ -424,7 +494,7 @@ const columns = ref<ColumnOption[]>([
{ key: 3, label: '部门', visible: true },
{ key: 4, label: '手机号码', visible: true },
{ key: 5, label: '状态', visible: true },
{ key: 6, label: '创建时间', visible: true }
{ key: 6, label: '创建时间', visible: true },
])
const upload = reactive({
@ -433,7 +503,7 @@ const upload = reactive({
isUploading: false,
updateSupport: 0,
headers: { Authorization: `Bearer ${getToken()}` },
url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData'
url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData',
})
/** 查询用户列表 */
@ -444,7 +514,7 @@ async function getList() {
if (dateRange.value && dateRange.value.length === 2) {
Object.assign(params, {
beginTime: dateRange.value[0],
endTime: dateRange.value[1]
endTime: dateRange.value[1],
})
}
const res: any = await getUserList(params)
@ -492,7 +562,7 @@ function handleNodeClick(data: any) {
/** 多选框选中数据 */
function handleSelectionChange(selection: any[]) {
ids.value = selection.map(item => item.userId)
ids.value = selection.map((item) => item.userId)
single.value = selection.length !== 1
multiple.value = !selection.length
}
@ -503,13 +573,15 @@ function handleStatusChange(row: any) {
ElMessageBox.confirm(`确认要"${text}""${row.userName}"用户吗?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await changeUserStatus(row.userId, row.status)
ElMessage.success(text + '成功')
}).catch(() => {
row.status = row.status === '0' ? '1' : '0'
type: 'warning',
})
.then(async () => {
await changeUserStatus(row.userId, row.status)
ElMessage.success(text + '成功')
})
.catch(() => {
row.status = row.status === '0' ? '1' : '0'
})
}
/** 更多操作 */
@ -530,11 +602,13 @@ function handleResetPwd(row: any) {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间'
}).then(async ({ value }) => {
await resetUserPwd(row.userId, value)
ElMessage.success('修改成功,新密码是:' + value)
}).catch(() => {})
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
})
.then(async ({ value }) => {
await resetUserPwd(row.userId, value)
ElMessage.success('修改成功,新密码是:' + value)
})
.catch(() => {})
}
/** 分配角色操作 */
@ -593,18 +667,23 @@ function handleDelete(row?: any) {
ElMessageBox.confirm(`是否确认删除用户编号为"${userIds}"的数据项?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await delUser(userIds)
ElMessage.success('删除成功')
getList()
}).catch(() => {})
type: 'warning',
})
.then(async () => {
await delUser(userIds)
ElMessage.success('删除成功')
getList()
})
.catch(() => {})
}
/** 导出按钮操作 */
function handleExport() {
// 使 window.location download
window.open(`${import.meta.env.VITE_APP_BASE_API}/system/user/export?userName=${queryParams.userName}&phonenumber=${queryParams.phonenumber}&status=${queryParams.status}`, '_blank')
window.open(
`${import.meta.env.VITE_APP_BASE_API}/system/user/export?userName=${queryParams.userName}&phonenumber=${queryParams.phonenumber}&status=${queryParams.status}`,
'_blank'
)
}
/** 导入按钮操作 */
@ -623,7 +702,11 @@ function handleFileSuccess(response: any) {
upload.open = false
upload.isUploading = false
uploadRef.value?.clearFiles()
ElMessageBox.alert(`<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>${response.msg}</div>`, '导入结果', { dangerouslyUseHTMLString: true })
ElMessageBox.alert(
`<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>${response.msg}</div>`,
'导入结果',
{ dangerouslyUseHTMLString: true }
)
getList()
}
@ -646,7 +729,7 @@ function reset() {
status: '0',
remark: '',
postIds: [],
roleIds: []
roleIds: [],
}
formRef.value?.resetFields()
}

Loading…
Cancel
Save