docs: add cell storage protocol modules design for 2.2.6–2.2.10

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-05-13 17:48:22 +08:00
1 parent 058811ae77
commit 246ad2228e
153 files changed
+20578 -2040

No files matched your search

+341
View File
@@ -0,0 +1,341 @@
<!-- 职位管理 -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="page-container">
<header class="page-head">
<h1>职位管理</h1>
<p class="page-desc">维护职位编码与名称。</p>
</header>
<section class="panel">
<div class="panel-top">
<h2 class="title-bar-heading">职位列表</h2>
</div>
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="职位名称/编码" clearable />
</el-form-item>
</div>
<div class="filter-actions-row">
<span class="filter-hint">按职位名称、编码模糊查询。</span>
<div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleReset">重置</el-button>
<el-button class="btn-main" type="primary" @click="handleSearch">搜索</el-button>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增职位
</el-button>
<el-button
class="btn-ghost"
type="danger"
:disabled="selectedRows.length === 0"
@click="handleBatchDelete"
>
批量删除
</el-button>
</div>
</div>
<div class="list-body list-wrap">
<el-table
class="list-table"
:data="tableData"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column prop="postName" label="职位名称" min-width="140" align="center" header-align="center" />
<el-table-column prop="postCode" label="职位编码" min-width="120" align="center" header-align="center" />
<el-table-column prop="status" label="状态" min-width="80" align="center" header-align="center">
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'danger'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderNum" label="排序" width="80" align="center" header-align="center" />
<el-table-column prop="createTime" label="创建时间" min-width="160" align="center" header-align="center" />
<el-table-column label="操作" width="260" fixed="right" align="center" header-align="center">
<template #default="{ row }">
<el-button class="btn-op-item primary" @click="handleEdit(row)">编辑</el-button>
<el-button class="btn-op-item" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination list-pager-bar">
<el-pagination
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
/>
</div>
</div>
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="560px"
class="dialog-form-layout"
align-center
destroy-on-close
:close-on-click-modal="false"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="post-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="职位名称" prop="postName">
<el-input v-model="formData.postName" placeholder="请输入职位名称" />
</el-form-item>
<el-form-item label="职位编码" prop="postCode">
<el-input v-model="formData.postCode" placeholder="请输入职位编码" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="formData.status" placeholder="请选择状态">
<el-option
v-for="opt in statusOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
<el-form-item label="排序">
<div class="sort-field">
<el-input-number
v-model="formData.orderNum"
:min="0"
:max="9999"
controls-position="right"
/>
<span class="form-tip">数值越小越靠前</span>
</div>
</el-form-item>
<el-form-item label="备注" style="grid-column: 1 / -1">
<el-input v-model="formData.remark" type="textarea" :rows="2" placeholder="可选" />
</el-form-item>
</div>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="dialogVisible = false">
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button class="btn-main" type="primary" @click="handleSubmit" :loading="submitLoading">
<el-icon><Check /></el-icon>
确定
</el-button>
</template>
</el-dialog>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Close, Check } from '@element-plus/icons-vue'
import {
fetchPostList,
fetchCreatePost,
fetchUpdatePost,
fetchDeletePost,
fetchDictDataOptions
} from '@/api/admin'
defineOptions({ name: 'SystemPost' })
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const tableData = ref<Api.Post.PostItem[]>([])
const selectedRows = ref<Api.Post.PostItem[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
const dialogVisible = ref(false)
const dialogTitle = ref('新增职位')
const isEdit = ref(false)
const submitLoading = ref(false)
const formRef = ref()
const editingId = ref<string | null>(null)
const formData = reactive({
postName: '',
postCode: '',
status: '1',
orderNum: 0,
remark: ''
})
const formRules = {
postName: [{ required: true, message: '请输入职位名称', trigger: 'blur' }],
postCode: [{ required: true, message: '请输入职位编码', trigger: 'blur' }]
}
const statusOptions = ref<Api.Dict.DictDataOption[]>([])
const loadDictOptions = async () => {
try {
const bmzt = await fetchDictDataOptions('bmzt')
statusOptions.value = bmzt || []
} catch (error) {
console.error('加载字典选项失败:', error)
}
}
const handleSelectionChange = (rows: Api.Post.PostItem[]) => {
selectedRows.value = rows
}
async function loadData() {
loading.value = true
try {
const res = await fetchPostList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
} finally {
loading.value = false
}
}
function handleSearch() {
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
pagination.page = 1
loadData()
}
function resetForm() {
formData.postName = ''
formData.postCode = ''
formData.status = '1'
formData.orderNum = 0
formData.remark = ''
editingId.value = null
}
function handleAdd() {
isEdit.value = false
dialogTitle.value = '新增职位'
resetForm()
dialogVisible.value = true
formRef.value?.clearValidate?.()
}
function handleEdit(row: Api.Post.PostItem) {
isEdit.value = true
dialogTitle.value = '编辑职位'
editingId.value = row.id
formData.postName = row.postName || ''
formData.postCode = row.postCode || ''
formData.status = row.status || '1'
formData.orderNum = row.orderNum ?? 0
formData.remark = row.remark || ''
dialogVisible.value = true
formRef.value?.clearValidate?.()
}
async function handleSubmit() {
await formRef.value?.validate?.()
submitLoading.value = true
try {
const payload = {
postName: formData.postName.trim(),
postCode: formData.postCode.trim(),
status: formData.status,
orderNum: formData.orderNum,
remark: formData.remark?.trim() || undefined
}
if (isEdit.value && editingId.value) {
await fetchUpdatePost(editingId.value, payload)
} else {
await fetchCreatePost(payload)
}
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
dialogVisible.value = false
await loadData()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
submitLoading.value = false
}
}
async function handleDelete(row: Api.Post.PostItem) {
await ElMessageBox.confirm(`确定删除职位「${row.postName}」吗?`, '提示', { type: 'warning' })
await fetchDeletePost(row.id)
ElMessage.success('删除成功')
await loadData()
}
async function handleBatchDelete() {
const ids = selectedRows.value.map((r) => r.id)
if (!ids.length) return
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 个职位吗?`, '提示', { type: 'warning' })
for (const id of ids) {
await fetchDeletePost(id)
}
selectedRows.value = []
await loadData()
}
onMounted(() => {
loadData()
loadDictOptions()
})
</script>
<style scoped lang="scss">
.post-dialog-form {
:deep(.el-form-item) {
margin-bottom: 0;
}
:deep(.el-form-item__label) {
font-size: 11px;
font-weight: 500;
color: var(--mint-text-muted);
padding: 0 0 4px;
line-height: 1.2;
}
}
</style>