123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- <template>
- <div class="app-container">
- <!-- 搜索栏 -->
- <el-form :model="queryParams" inline class="search-form">
- <el-form-item label="申请人">
- <el-input
- v-model="queryParams.applicant"
- placeholder="请输入申请人"
- clearable
- @keyup.enter="handleSearch"
- />
- </el-form-item>
- <el-form-item label="状态">
- <el-select
- v-model="queryParams.status"
- placeholder="全部状态"
- clearable
- >
- <el-option
- v-for="item in statusOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleSearch">查询</el-button>
- <el-button @click="resetQuery">重置</el-button>
- <el-button
- type="primary"
- @click="handleCreate"
- v-permission="'apply:create'"
- >
- 新建申请
- </el-button>
- </el-form-item>
- </el-form>
- <!-- 数据表格 -->
- <el-table
- :data="tableData"
- border
- v-loading="loading"
- element-loading-text="数据加载中..."
- >
- <el-table-column prop="applyNo" label="申请单号" width="180" fixed />
- <el-table-column prop="applicant" label="申请人" width="120" />
- <el-table-column prop="applyTime" label="申请时间" width="180" sortable />
- <el-table-column prop="specimenName" label="标本名称" min-width="150" />
- <el-table-column prop="specimenNo" label="标本编号" width="120" />
- <el-table-column prop="purpose" label="用途" show-overflow-tooltip />
- <el-table-column label="附件" width="120">
- <template #default="{ row }">
- <div v-if="row.attachments?.length">
- <el-tooltip
- v-for="(file, index) in row.attachments"
- :key="index"
- :content="file.name"
- >
- <el-link
- type="primary"
- :href="file.url"
- target="_blank"
- class="file-link"
- >
- <el-icon><Document /></el-icon>
- </el-link>
- </el-tooltip>
- </div>
- <span v-else>--</span>
- </template>
- </el-table-column>
- <el-table-column prop="status" label="状态" width="100">
- <template #default="{ row }">
- <el-tag
- :type="statusStyleMap[row.status]"
- effect="light"
- >
- {{ statusMap[row.status] }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="150" fixed="right">
- <template #default="{ row }">
- <el-button
- v-if="row.status === 0"
- type="danger"
- link
- @click="handleWithdraw(row)"
- v-permission="'apply:withdraw'"
- >
- 撤回
- </el-button>
- <el-button
- type="primary"
- link
- @click="handleDetail(row)"
- >
- 详情
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <el-pagination
- v-model:current-page="queryParams.pageNum"
- v-model:page-size="queryParams.pageSize"
- :total="total"
- :page-sizes="[10, 20, 50, 100]"
- layout="total, sizes, prev, pager, next, jumper"
- @size-change="getList"
- @current-change="getList"
- class="pagination"
- />
- <!-- 申请弹窗 -->
- <el-dialog
- v-model="dialogVisible"
- :title="dialogTitle"
- width="600px"
- :close-on-click-modal="false"
- >
- <el-form
- ref="formRef"
- :model="formData"
- :rules="rules"
- label-width="100px"
- label-position="right"
- >
- <el-form-item label="标本选择" prop="specimenId">
- <el-select
- v-model="formData.specimenId"
- placeholder="请选择标本"
- filterable
- remote
- :remote-method="searchSpecimen"
- :loading="specimenLoading"
- style="width: 100%"
- >
- <el-option
- v-for="item in specimenList"
- :key="item.id"
- :label="`${item.name} (${item.number})`"
- :value="item.id"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="用途说明" prop="purpose">
- <el-input
- v-model="formData.purpose"
- type="textarea"
- :rows="4"
- placeholder="请输入详细的用途说明"
- show-word-limit
- maxlength="500"
- />
- </el-form-item>
- <el-form-item label="附件">
- <el-upload
- v-model:file-list="fileList"
- action="/api/upload"
- multiple
- :limit="3"
- :on-exceed="handleExceed"
- :before-upload="beforeUpload"
- :on-success="handleUploadSuccess"
- :on-error="handleUploadError"
- list-type="text"
- >
- <el-button type="primary">选择文件</el-button>
- <template #tip>
- <div class="upload-tip">
- 支持格式:PDF/DOC/DOCX/图片,单个文件不超过10MB
- </div>
- </template>
- </el-upload>
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button
- type="primary"
- @click="submitForm"
- :loading="submitting"
- >
- {{ submitting ? '提交中...' : '确认提交' }}
- </el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup>
- import { ref, reactive, onMounted } from 'vue'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { Document } from '@element-plus/icons-vue'
- // 状态字典
- const statusMap = {
- 0: '待审核',
- 1: '已通过',
- 2: '已驳回',
- 3: '已撤回'
- }
- const statusStyleMap = {
- 0: 'warning',
- 1: 'success',
- 2: 'danger',
- 3: 'info'
- }
- const statusOptions = [
- { label: '全部', value: '' },
- { label: '待审核', value: 0 },
- { label: '已通过', value: 1 },
- { label: '已驳回', value: 2 },
- { label: '已撤回', value: 3 }
- ]
- // 数据加载状态
- const loading = ref(false)
- const submitting = ref(false)
- const specimenLoading = ref(false)
- // 表格数据
- const tableData = ref([])
- const total = ref(0)
- // 查询参数
- const queryParams = reactive({
- pageNum: 1,
- pageSize: 10,
- applicant: '',
- status: ''
- })
- // 表单数据
- const formData = reactive({
- specimenId: '',
- purpose: '',
- attachments: []
- })
- // 标本列表
- const specimenList = ref([])
- // 文件列表
- const fileList = ref([])
- // 表单验证规则
- const rules = {
- specimenId: [
- { required: true, message: '请选择标本', trigger: 'blur' }
- ],
- purpose: [
- { required: true, message: '请输入用途说明', trigger: 'blur' },
- { min: 10, message: '至少输入10个字符', trigger: 'blur' }
- ]
- }
- // 初始化加载
- onMounted(() => {
- getList()
- loadSpecimenList()
- })
- // 获取申请列表
- const getList = async () => {
- try {
- loading.value = true
- // 调用API接口
- const res = await fetchApplications(queryParams)
- tableData.value = res.data.list
- total.value = res.data.total
- } catch (error) {
- ElMessage.error('数据加载失败')
- } finally {
- loading.value = false
- }
- }
- // 搜索标本
- const searchSpecimen = async (query) => {
- if (!query) return
- try {
- specimenLoading.value = true
- const res = await searchSpecimens({ keyword: query })
- specimenList.value = res.data
- } finally {
- specimenLoading.value = false
- }
- }
- // 提交表单
- const submitForm = async () => {
- try {
- await formRef.value.validate()
- submitting.value = true
- // 处理附件
- const attachments = fileList.value.map(file => ({
- name: file.name,
- url: file.response?.data.url || ''
- }))
- await createApplication({
- ...formData,
- attachments
- })
- ElMessage.success('提交成功')
- dialogVisible.value = false
- getList()
- } catch (error) {
- console.error('提交失败:', error)
- } finally {
- submitting.value = false
- }
- }
- // 撤回申请
- const handleWithdraw = async (row) => {
- try {
- await ElMessageBox.confirm('确定要撤回该申请吗?', '提示', {
- type: 'warning'
- })
- await withdrawApplication(row.id)
- ElMessage.success('已撤回申请')
- getList()
- } catch (error) {
- if (error !== 'cancel') {
- ElMessage.error('撤回失败')
- }
- }
- }
- // 文件上传处理
- const beforeUpload = (file) => {
- const isAllowed = ['image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
- .includes(file.type)
- const isLt10M = file.size / 1024 / 1024 < 10
- if (!isAllowed) {
- ElMessage.error('不支持的文件格式!')
- return false
- }
- if (!isLt10M) {
- ElMessage.error('文件大小不能超过10MB!')
- return false
- }
- return true
- }
- </script>
- <style scoped>
- .search-form {
- margin-bottom: 20px;
- }
- .file-link {
- margin-right: 8px;
- }
- .upload-tip {
- color: #666;
- font-size: 12px;
- margin-top: 8px;
- }
- .pagination {
- margin-top: 20px;
- justify-content: flex-end;
- }
- </style>
|