add 原因管理界面

This commit is contained in:
Asoka 2025-06-11 19:17:37 +08:00
parent ff112d066d
commit eb3307594a
5 changed files with 608 additions and 142 deletions

View File

@ -8429,3 +8429,121 @@ export interface ResultOutputBagDtoGetOutput {
message?: string | null message?: string | null
data?: BagDto data?: BagDto
} }
/** 撤销原因分页查询输入 */
export interface PageInputRevokeReasonGetPageInput {
dynamicFilter?: DynamicFilterInfo
/** 排序列表 */
sortList?: SortInput[] | null
/**
*
* @format int32
*/
currentPage?: number
/**
*
* @format int32
*/
pageSize?: number
/** 分页请求 */
filter?: RevokeReasonGetPageInput
}
/** 撤销原因分页查询过滤条件 */
export interface RevokeReasonGetPageInput {
/** 原因描述 */
revokeReason?: string | null
/** 类型 */
revokeType?: string | null
/** 是否下发 */
isDown?: boolean
}
/** 撤销原因分页查询输出 */
export interface RevokeReasonGetPageOutput {
/**
*
* @format int64
*/
id?: number
/** 原因描述 */
revokeReason?: string | null
/** 类型 */
revokeType?: string | null
/** 是否下发 */
isDown?: boolean
/** 创建者 */
createdUserName?: string | null
/**
*
* @format date-time
*/
createdTime?: string | null
/**
*
* @format date-time
*/
modifiedTime?: string | null
}
export interface ResultOutputPageOutputRevokeReasonGetPageOutput {
success?: boolean
code?: string | null
message?: string | null
data?: PageOutputBagDtoGetPageOutput
}
export interface PageOutputRevokeReasonGetPageOutput {
list?: RevokeReasonGetPageOutput[] | null
total?: number
}
/** 撤销原因详情输出 */
export interface RevokeReasonGetOutput {
/** 原因描述 */
revokeReason?: string | null
/** 类型 */
revokeType?: string | null
/** 是否下发 */
isDown?: boolean
/**
* Id
* @format int64
*/
id: number
}
/** 撤销原因新增/更新输入 */
export interface RevokeReasonDto {
/** 原因描述 */
keyWord?: string | null
revokeReason?: string | null
/** 类型 */
revokeType?: string | null
/** 是否下发 */
isDown?: boolean
/**
* Id
* @format int64
*/
id?: number
}
/** 撤回类型枚举项 */
export interface RevokeTypeEnumItem {
/** 枚举值 */
value: number
/** 枚举名称 */
name: string
/** 显示名称/描述 */
label: string
}
/** 撤回类型枚举列表输出 */
export interface RevokeTypeEnumListOutput {
success?: boolean
code?: string | null
message?: string | null
data?: RevokeTypeEnumItem[]
}

View File

@ -0,0 +1,84 @@
/* eslint-disable */
/* tslint:disable */
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: adademo / https://github.com/adademo/swagger-typescript-api ##
* ## SOURCE: https://github.com/adademo/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { PageInputRevokeReasonGetPageInput, RevokeReasonDto, RevokeReasonGetOutput,ResultOutputPageOutputRevokeReasonGetPageOutput, RevokeTypeEnumListOutput } from './data-contracts'
import { RequestParams } from './http-client'
import { ContentType, HttpClient } from './http-client'
export class RevokeReasonApi extends HttpClient {
/**
*
*/
getPage = (data: PageInputRevokeReasonGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputRevokeReasonGetPageOutput>({
path: `/api/admin/revoke-reason/get-page`,
method: 'POST',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*/
get = (params: { id: number }, requestParams: RequestParams = {}) =>
this.request<RevokeReasonGetOutput>({
path: `/api/admin/revoke-reason/get`,
method: 'GET',
query: params,
...requestParams,
})
/**
*
*/
add = (data: RevokeReasonDto, params: RequestParams = {}) =>
this.request<any>({
path: `/api/admin/revoke-reason/add`,
method: 'POST',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*/
update = (data: RevokeReasonDto, params: RequestParams = {}) =>
this.request<any>({
path: `/api/admin/revoke-reason/update`,
method: 'PUT',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*/
softDelete = (params: { id: number }, requestParams: RequestParams = {}) =>
this.request<any>({
path: `/api/admin/revoke-reason/soft-delete`,
method: 'DELETE',
query: params,
...requestParams,
})
/**
*
*/
getRevokeTypeEnumList = (params: RequestParams = {}) =>
this.request<RevokeTypeEnumListOutput>({
path: `/api/admin/revoke-reason/get-revoke-type-enum-list`,
method: 'GET',
...params,
})
}

View File

@ -0,0 +1,149 @@
<template>
<div class="uspreason-form">
<el-dialog
v-model="state.visible"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" size="default">
<div class="form-section">
<el-row :gutter="25">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="原因类型" prop="revokeType">
<el-select v-model="form.revokeType" placeholder="请选择原因类型" style="width: 100%">
<el-option
v-for="item in state.revokeTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="原因描述" prop="revokeReason">
<el-input
v-model="form.revokeReason"
type="textarea"
:rows="3"
placeholder="请输入原因描述"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="state.loading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, defineProps, defineExpose, onMounted } from 'vue'
import { RevokeReasonApi } from '/@/api/admin/revoke-reason'
import type { RevokeReasonDto, RevokeTypeEnumItem } from '/@/api/admin/data-contracts'
import eventBus from '/@/utils/mitt'
const props = defineProps<{ title: string }>()
const emit = defineEmits(['ok'])
const formRef = ref()
const state = reactive({
visible: false,
loading: false,
revokeTypeOptions: [] as RevokeTypeEnumItem[]
})
const form = reactive<RevokeReasonDto>({
revokeReason: '',
revokeType: '',
isDown: false,
id: 0
})
const rules = {
revokeReason: [{ required: true, message: '请输入原因描述', trigger: 'blur' }],
revokeType: [{ required: true, message: '请选择类型', trigger: 'change' }]
}
//
const getRevokeTypeOptions = async () => {
try {
const res = await new RevokeReasonApi().getRevokeTypeEnumList()
if (res?.success && res.data) {
state.revokeTypeOptions = res.data
}
} catch (error) {
console.error('获取撤回类型枚举列表失败:', error)
}
}
function open(row?: Partial<RevokeReasonDto>) {
if (row) {
Object.assign(form, row)
} else {
Object.assign(form, { revokeReason: '', revokeType: '', isDown: false, id: 0 })
}
state.visible = true
}
function onCancel() {
state.visible = false
}
function onSubmit() {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.loading = true
try {
if (form.id) {
await new RevokeReasonApi().update(form)
} else {
await new RevokeReasonApi().add(form)
}
state.visible = false
eventBus.emit('refreshUspReason')
} finally {
state.loading = false
}
})
}
onMounted(() => {
getRevokeTypeOptions()
})
defineExpose({ open })
</script>
<style lang="scss" scoped>
.form-section {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
}
:deep(.el-form-item) {
margin-bottom: 18px;
}
:deep(.el-switch__label) {
font-size: 13px;
}
:deep(.el-textarea__inner) {
resize: none;
}
</style>

View File

@ -0,0 +1,257 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form ref="queryForm" :model="queryParams" :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="原因描述" prop="revokeReason">
<el-input v-model="queryParams.filter.keyWord" placeholder="请输入原因描述" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="原因类型" prop="revokeType">
<el-select v-model="queryParams.filter.revokeType" placeholder="请选择原因类型" clearable style="width: 200px">
<el-option v-for="item in revokeTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker
v-model="queryParams.filter.startTime"
type="datetime"
placeholder="选择开始时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 180px"
/>
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker
v-model="queryParams.filter.endTime"
type="datetime"
placeholder="选择结束时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 180px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="handleQuery">查询</el-button>
<el-button v-auth="'api:admin:revoke-reason:add'" type="primary" icon="ele-Plus" @click="handleAdd">新增</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="loading" :data="revokeReasonList" row-key="id" style="width: 100%" border>
<el-table-column label="原因类型" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
{{ getRevokeTypeName(row.revokeType) }}
</template>
</el-table-column>
<el-table-column prop="revokeReason" label="原因描述" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:revoke-reason:update'" icon="ele-EditPen" size="small" text type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:revoke-reason:soft-delete'" icon="ele-Delete" size="small" text type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="queryParams.currentPage"
v-model:page-size="queryParams.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
size="small"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<uspreason-form ref="formRef" :title="state.formTitle" @ok="getList" />
</my-layout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onBeforeMount } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { RevokeReasonApi } from '/@/api/admin/revoke-reason'
import eventBus from '/@/utils/mitt'
import type {
RevokeReasonGetPageOutput,
RevokeTypeEnumItem,
RevokeTypeEnumListOutput,
PageOutputRevokeReasonGetPageOutput
} from '/@/api/admin/data-contracts'
import UspreasonForm from './components/uspreason-form.vue'
import { number } from 'echarts'
const loading = ref(false)
const showSearch = ref(true)
const total = ref(0)
const revokeReasonList = ref<RevokeReasonGetPageOutput[]>([])
const revokeTypeOptions = ref<RevokeTypeEnumItem[]>([])
const formRef = ref()
const dateRange = ref<[string, string] | null>(null)
const state = reactive({
loading: false,
formTitle: ''
})
const queryParams = reactive({
currentPage: 1,
pageSize: 10,
filter: {
keyWord: '',
revokeType: '',
isDown: false,
startTime: '',
endTime: ''
}
})
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const res = await new RevokeReasonApi().getPage(queryParams)
console.log(res)
revokeReasonList.value = res.data?.list || []
total.value = res.data?.total || 0
} catch (error) {
console.error('获取列表失败:', error)
} finally {
loading.value = false
}
}
/** 获取撤销类型枚举列表 */
const getRevokeTypeOptions = async () => {
try {
const res = await new RevokeReasonApi().getRevokeTypeEnumList()
revokeTypeOptions.value = res.data ?? []
} catch (error) {
console.error('获取撤销类型枚举列表失败:', error)
}
}
const getRevokeTypeName = (revokeType: string) => {
const item = revokeTypeOptions.value.find(item => item.value === Number(revokeType))
return item?.label || ''
}
/** 处理日期范围变化 */
const handleDateRangeChange = (val: [string, string] | null) => {
if (val) {
queryParams.filter.startTime = val[0]
queryParams.filter.endTime = val[1]
} else {
queryParams.filter.startTime = ''
queryParams.filter.endTime = ''
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.currentPage = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryParams.filter = {
keyWord: '',
revokeType: '',
isDown: false,
startTime: '',
endTime: ''
}
handleQuery()
}
/** 新增按钮操作 */
const handleAdd = () => {
state.formTitle = '新增原因'
formRef.value?.open()
}
/** 修改按钮操作 */
const handleEdit = (row: RevokeReasonGetPageOutput) => {
state.formTitle = '编辑原因'
formRef.value?.open(row)
}
/** 删除按钮操作 */
const handleDelete = (row: RevokeReasonGetPageOutput) => {
ElMessageBox.confirm('确认要删除该记录吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await new RevokeReasonApi().softDelete({ id: row.id! })
if (res.success) {
ElMessage.success('删除成功')
getList()
}
} catch (error) {
console.error('删除失败:', error)
}
})
}
/** 每页条数改变 */
const handleSizeChange = (val: number) => {
queryParams.pageSize = val
getList()
}
/** 当前页改变 */
const handleCurrentChange = (val: number) => {
queryParams.currentPage = val
getList()
}
onMounted(() => {
getRevokeTypeOptions()
getList()
eventBus.on('refreshUspReason', getList)
})
onBeforeMount(() => {
eventBus.off('refreshUspReason')
})
</script>
<style lang="scss" scoped>
.my-query-box {
:deep(.el-form-item) {
margin-bottom: 16px;
}
}
.my-fill {
flex: 1;
display: flex;
flex-direction: column;
}
.my-flex {
display: flex;
}
.my-flex-end {
justify-content: flex-end;
}
.mt8 {
margin-top: 8px;
}
.mb8 {
margin-bottom: 8px;
}
</style>

View File

@ -1,142 +0,0 @@
# 页面开发指南
## 页面结构
1. 列表页面
- 搜索区域:关键词搜索
- 操作按钮:新增、查询
- 表格区域:基础字段展示
- 分页区域:标准分页组件
2. 编辑页面
- 基本信息模块
- 网络配置模块
- 设备配置模块
## 样式规范
1. 对话框配置
```html
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="900px"
>
```
2. 表单布局
```html
<el-form :model="form" ref="formRef" size="default" label-width="120px">
<div class="form-section">
<div class="section-title">模块标题</div>
<el-row :gutter="25">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="字段名称" prop="fieldName">
<el-input v-model="form.fieldName" clearable placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
```
3. 样式定义
```scss
.form-section {
margin-bottom: 24px;
&:last-child { margin-bottom: 0; }
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid #f0f0f0;
position: relative;
&:before {
content: '';
position: absolute;
left: 0;
bottom: -2px;
width: 40px;
height: 2px;
background: #409eff;
}
}
```
## 开发规范
1. 组件命名
- 目录:小写字母,用横线分隔
- 组件PascalCase
- 组合函数camelCase
2. 代码组织
```typescript
// 1. 导入声明
import { reactive, ref } from 'vue'
// 2. 类型定义
interface State {
showDialog: boolean
form: FormData
}
// 3. 组件定义
const props = defineProps({
title: String
})
// 4. 状态定义
const state = reactive<State>({
showDialog: false,
form: {}
})
// 5. 方法定义
const handleSubmit = async () => {
// 处理逻辑
}
```
3. 表单验证
```typescript
const rules = {
fieldName: [
{ required: true, message: '请输入', trigger: ['blur', 'change'] }
]
}
```
4. API 调用
```typescript
const handleSave = async () => {
try {
const res = await api.save(state.form)
if (res.success) {
ElMessage.success('保存成功')
emit('refresh')
state.showDialog = false
}
} catch (error) {
console.error(error)
}
}
```
## 注意事项
1. 保持与 uspscale 页面风格一致
2. 使用响应式布局适配不同屏幕
3. 统一表单验证规则和错误提示
4. 优化用户交互体验
5. 保持代码风格统一
6. 注意性能优化
7. 遵循 TypeScript 类型规范
---
**注意**:此模板基于 Vue 3 + TypeScript + Element Plus + Pinia 技术栈,使用时请根据实际项目技术栈进行调整。