vue3 encapsulates the table pop-up component - table radio selection, axios dynamic interface, toRefs() usage, loading customization, table content exceeding the height scroll
1
2
1. Click the page
index.vue
<template>
<div class="main-content">
<el-form ref="formRef" label-width="130" :model="formInline.formData">
<el-row>
<el-col :span="6">
<el-form-item
label="项目名称:"
prop="prjName"
:rules="{
required: true,
message: '请选择项目',
trigger: 'change',
}"
>
<el-input
v-model="formInline.formData.prjName"
:disabled="route.query.type === 'view'"
maxlength="100"
placeholder="请输入"
@click="handlePrjName"
@input="
(e) => (formInline.formData.prjName = replaceCommonText(e))
"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="牵头单位:" prop="leadUnitName">
<el-input
v-model="formInline.formData.leadUnitName"
disabled="false"
maxlength="100"
placeholder="请输入"
@input="
(e) => (formInline.formData.leadUnitName = replaceCommonText(e))
"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目负责人:" prop="prjHeadName">
<el-input
v-model="formInline.formData.prjHeadName"
disabled="false"
maxlength="100"
placeholder="请输入"
@input="
(e) => (formInline.formData.prjHeadName = replaceCommonText(e))
"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="系统内单位角色:" prop="innerRole">
<dictSelect
v-model="formInline.formData.innerRole"
:dictid="'sgCompRole'"
disabled="false"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<SelectProject
v-if="dialogProject"
v-model:dataType="dataType"
v-model:dialogVisible="dialogProject"
v-model:prjData="formInline.formData"
:prj-category="2"
:request-url="mediumExamSelectPrj"
:selected="formInline.formData.prjCode"
@emit-select="handleChooseData"
/>
</div>
</template>
<script setup>
import SelectProject from "@/components/SelectProject";
import { mediumExamSelectPrj } from "@/api/project/couny";
const formInline = reactive({
formData: {},
});
const dialogProject = ref(false);
const dataType = ref("project");
const handlePrjName = () => {
dialogProject.value = true;
};
const handleChooseData = (val) => {
formInline.formData.prjId = val[0].id;
};
</script>
<style lang="scss" scoped></style>
2. Home page pop-up component
src\app\science\components\SelectProject.vue
<!--
dialogVisible // 弹窗显隐
dataType // 弹窗数据类型 project项目数据
prjData // 选择后项目数据
prjCategory // 项目分类 1 公司级,2 国家级,3 各单位自管, 100可研申报
emit-select // 项目选择后回调方法
requestUrl // 新的项目请求接口
-->
<template>
<el-dialog
v-model="dialogVisible"
class="diaStyle"
:modal="false"
style="height: 80vh; overflow-y: auto"
:title="dataType === 'project' ? '选择项目' : ''"
>
<el-form
ref="formRef"
class="inline-form"
:inline="true"
label-position="right"
label-width="100px"
:model="formInline"
>
<el-form-item label="项目名称:" prop="prjName">
<el-input
v-model="formInline.prjName"
maxlength="100"
placeholder="请输入"
:rules="[
{
validator: validateCommonText,
trigger: ['blur', 'change'],
},
]"
@input="(e) => (formInline.prjName = replaceCommonText(e))"
/>
</el-form-item>
<el-form-item
label="牵头单位:"
prop="leadUnit"
:rules="[
{
validator: validateCommonText,
trigger: ['blur', 'change'],
},
]"
>
<unitSelect v-model="formInline.leadUnit" />
</el-form-item>
<el-form-item class="search_btn" style="float: right">
<el-button v-throttle="3000" type="primary" @click="onSubmit">
查询
</el-button>
<el-button v-throttle="3000" plain type="primary" @click="resetForm">
重置
</el-button>
</el-form-item>
</el-form>
<el-table
ref="multipleTable"
border
class="dialog-table"
:data="tableData"
highlight-current-row
stripe
@row-click="onSelect"
@select-all="onSelectAll"
@selection-change="selectItem"
>
<el-table-column width="55">
<template #default="{ row }">
<el-radio v-model="templateSelete" :label="row.id">{
{ '' }}</el-radio>
</template>
</el-table-column>
<el-table-column label="项目名称" prop="prjName" />
<el-table-column label="牵头单位" prop="leadUnitName" />
<el-table-column label="项目负责人" prop="prjHeadName" />
<el-table-column label="经费分配">
<template #default="scope">
<el-input
v-if="scope.row.editType"
v-model="scope.row.fee"
maxlength="100"
@click="selectFunds(scope.$index, scope.row)"
@input="(e) => (scope.row.fee = replaceCommonText(e))"
/>
<div v-else class="contain">
<p v-for="item in scope.row.yunitList" :key="item">
{
{ item.bearUnitName }}:{
{ item.payIn }}
</p>
</div>
</template>
</el-table-column>
<el-table-column label="项目标签">
<template #default="{ row }">
<div v-if="!row.isEdit">
<span v-for="(item, index) in row.labelVOList" :key="item">
{
{ item.labelName }}
<!-- 最后一个标签尾部没有逗号 -->
<span v-if="index !== row.labelVOList.length - 1">,</span>
</span>
</div>
<el-select
v-else
v-model="row.labelVOListV2"
multiple
placeholder="请选择"
@change="selectChange"
>
<el-option
v-for="item in option"
:key="item.id"
:label="item.labelName"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
</el-table>
<el-pagination
background
:current-page="page.currentPage"
layout="total,size,prev,pager,next,jumper"
:page-size="page.pageSize"
:total="page.total"
@current-change="hanleCurrentChange"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitDialog">确定</el-button>
<el-button plain type="primary" @click="cancelDialog">取消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { projectData, feasStuPage } from "@/api/project/couny";
import unitSelect from '@src/components/unitSelect'
import { validateCommonText, replaceCommonText } from '@src/utils/validate'
const multipleTable = ref(null)
const formRef = ref(null)
const multipleSelection = ref()
const props = defineProps({
dialogVisible: {
default: null,
type: Boolean,
},
selected: {
default: '',
type: String,
},
dataType: {
default: null,
type: String,
},
prjData: {
default: null,
type: Object,
},
prjCategory: {
default: null,
type: Number,
},
requestUrl: {
default: null,
type: Function,
},
})
const { dataType, prjCategory, requestUrl } = toRefs(props)
const emit = defineEmits([
'update:dialogVisible',
'update:prjData',
'emit-select',
])
const dialogVisible = computed({
get: () => props.dialogVisible,
set: (val) => emit('update:dialogVisible', val),
})
const page = reactive({
pageSize: 10,
currentPage: 1,
total: 0,
})
//const templateSelete = ref('')
const templateSelete = ref(props.selected)
const tableData = ref([])
//查询
const formInline = ref({
prjName: '',
techFieldName: '',
leadUnit: '',
})
if (dialogVisible) {
formInline.value = {
prjName: '',
techFieldName: '',
leadUnit: '',
}
}
//重置
const resetForm = () => {
// if (!formEl) return
formRef.value.resetFields()
onLoad()
}
const onSubmit = async () => {
page.currentPage = 1
onLoad()
}
const hanleCurrentChange = (val) => {
page.currentPage = val
onLoad()
}
const onLoad = async () => {
const formData = {
size: page.pageSize,
current: page.currentPage,
prjCategory: prjCategory.value,
...formInline.value,
state: '4',
}
const loadingInstance = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
if (requestUrl.value) {
await requestUrl
.value(formData)
.then((res) => {
tableData.value = res.data.data
page.pageSize = res.data.pageSize
page.total = Number(res.data.total)
page.currentPage = res.data.pageNumber
loadingInstance.close()
})
.catch(() => {
loadingInstance.close()
})
} else if (prjCategory.value == 100) {
formData.processState = '4'
formData.leadUnitId = formData.leadUnit
await feasStuPage(formData)
.then((res) => {
res.data.records.forEach((el) => {
el.leadUnitName = el.leadUnitId
el.prjHeadName = el.headName
})
tableData.value = res.data.records
page.pageSize = res.data.pageSize
page.total = res.data.total
page.currentPage = res.data.pageNumber
loadingInstance.close()
})
.catch(() => {
loadingInstance.close()
})
} else {
await projectData(formData)
.then((res) => {
tableData.value = res.data.records
page.pageSize = res.data.pageSize
page.total = res.data.total
page.currentPage = res.data.pageNumber
loadingInstance.close()
tableData.value.forEach((el) => {
if(el.prjCode == props.selected){
templateSelete.value = el.id
multipleTable.value = []
multipleTable.value.push(el)
}
})
})
.catch(() => {
loadingInstance.close()
})
}
}
onLoad()
const onSelectAll = () => {
multipleTable.value.clearSelection()
}
//下拉
const selectChange = (val) => {
console.log('val', val)
}
const selectItem = (rows) => {
if (rows.length > 1) {
const newRows = rows.filter((it, index) => {
if (index === rows.length - 1) {
multipleTable.value.toggleRowSelection(it, true)
return true
} else {
multipleTable.value.toggleRowSelection(it, false)
return false
}
})
multipleSelection.value = newRows
} else {
multipleSelection.value = rows
}
}
const onSelect = (row) => {
templateSelete.value = row.id
multipleTable.value.clearSelection()
multipleTable.value.toggleRowSelection(row, true)
multipleSelection.value = []
multipleSelection.value.push(row)
}
const cancelDialog = () => {
emit('update:dialogVisible', false)
}
const submitDialog = async () => {
if (multipleSelection.value) {
multipleSelection.value[0].prjId = multipleSelection.value[0].id
emit('update:prjData', multipleSelection.value[0])
emit('update:dialogVisible', false)
emit('emit-select', multipleSelection.value)
} else {
ElMessage.error('请选择项目')
}
}
</script>
<style lang="scss" scoped>
.dialog-project {
height: 80vh;
overflow-y: auto;
}
.dialog-footer {
text-align: center;
}
.contain {
height: 70px !important;
overflow-y: auto !important; //表格内容超出高度滚动
p {
line-height: 14px;
}
}
</style>
Home page interface
import request from '@src/utils/request'
import {
sciencePostUrl } from '@/config'
//项目中期项目选择列表
export const mediumExamSelectPrj = (data) => {
return request({
url: `${
sciencePostUrl}/mediumExam/selectPrj`,
method: 'post',
data,
})
}
//变更管理-项目-选择项目审核通过的
export const projectData = (data) => {
return request({
url: `${
sciencePostUrl}/prjChange/queryItem`,
method: 'post',
data,
})
}
//可研申报-项目-选择项目审核通过的
export const feasStuPage = (data) => {
return request({
url: `${
sciencePostUrl}/feasStu/feasStuPage`,
method: 'post',
data,
})
}
form validation
src\utils\validate.ts
/**
* @description form表单特定字符校验
* @param value
* @returns {boolean}
*/
export function validateCommonText(rule: any, value: any, callback: any) {
const err: any = validatorSpecialCharacter(value)
if (err) {
callback(new Error(err.message))
return
}
callback()
}
// 禁止输入框特殊字符校验
export function replaceCommonText(e: any) {
const err: any = validatorSpecialCharacter(e)
if (err) {
ElMessage({
message: err.message,
type: 'warning',
})
const y = e.replace(e, '')
return y
} else {
return e
}
}
3. Drop-down component
src\components\unitSelect.vue
<!--
modelValue //select的值 code值
disabled //是否编辑
contrUnitName //回显是select的name 值
change-unit //回调方法: 参数为选择后单位相关的数据
labelCode //二级单位数据传对应labelCode
orgcode //组织树下拉数据筛选条件
-->
<template>
<div style="width: 100%">
<el-select
v-if="labelCode"
v-model="unitValue"
:disabled="disabled"
filterable
placeholder="请选择单位"
@change="selectUnitEvent"
>
<el-option
v-for="item in companyLabelData"
:key="item.id"
:label="item.orgObjMdmName"
:value="item.id"
/>
</el-select>
<el-select
v-else-if="orgcode"
v-model="unitValue"
:disabled="disabled"
filterable
placeholder="请选择单位"
@change="selectUnitEvent"
>
<el-option
v-for="item in companyData"
:key="item.id"
:label="item.orgObjMdmName"
:value="item.id"
/>
</el-select>
<el-select
v-else
v-model="unitValue"
clearable
:disabled="disabled"
filterable
:loading="loading"
placeholder="请搜索单位"
remote
:remote-method="remoteMethod"
reserve-keyword
@change="selectUnitEvent"
>
<template #prefix>
<el-icon class="el-input__icon"><search /></el-icon>
</template>
<el-option
v-for="item in companyData"
:key="item.id"
:label="item.orgObjMdmName"
:value="item.id"
/>
</el-select>
</div>
</template>
<script setup>
import { getUnits, getLabelUnits } from '@src/api/common/base'
import { Search } from '@element-plus/icons-vue'
const props = defineProps({
modelValue: {
default: null,
type: Number,
},
contrUnitName: {
default: null,
type: String,
},
labelCode: {
default: null,
type: Array,
},
disabled: {
default: false,
type: Boolean,
},
orgcode: {
default: '',
type: String,
},
})
const ruleForm = ref({
orgObjMdmNameLike: '',
current: '1',
size: 100,
})
const { labelCode, disabled, orgcode } = toRefs(props)
const emit = defineEmits(['update:modelValue', 'change-unit'])
const companyData = ref()
const companyLabelData = ref()
onMounted(() => {
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
nextTick(() => {
if (props?.labelCode && !companyLabelData.value?.length) {
getLabelUnitsList()
} else if (props?.orgcode && !companyData.value?.length) {
getUnitsList()
} else if (!props.orgcode) {
companyData.value = [
{
orgObjMdmName: props.contrUnitName,
id: props.modelValue,
},
]
}
})
}
},
{ immediate: true }
)
})
//接口文档调取
const getUnitsList = async () => {
ruleForm.value.porgObjCodeInner = orgcode.value
const pattern = /[_]$/
if (!pattern.test(ruleForm.value.orgObjMdmNameLike)) {
await getUnits(ruleForm.value).then((res) => {
companyData.value = res.data.list
})
}
}
const getLabelUnitsList = async () => {
await getLabelUnits({ labelCode: labelCode.value }).then((res) => {
companyLabelData.value = res.data
})
}
if (labelCode.value && labelCode.value.length) {
getLabelUnitsList()
} else if (orgcode.value && orgcode.value.length) {
getUnitsList()
}
const unitValue = toRef(props, 'modelValue')
const selectUnitEvent = (val) => {
emit('update:modelValue', val)
let obj
if (labelCode.value && labelCode.value.length) {
obj = val
? companyLabelData.value.find(function (item) {
return item.id === val
})
: { id: '', orgObjMdmName: '' }
} else {
obj = val
? companyData.value.find(function (item) {
return item.id === val
})
: { id: '', orgObjMdmName: '' }
}
emit('change-unit', obj)
}
//过滤筛选关键字数据
const loading = ref(false)
const remoteMethod = (query) => {
if (query) {
ruleForm.value.orgObjMdmNameLike = query
loading.value = true
setTimeout(() => {
loading.value = false
getUnitsList()
}, 200)
} else {
companyData.value = []
}
}
</script>
unit interface
src\api\common\base.js
import request from '@src/utils/request'
//枚举值查询 process.env.VUE_APP_URL--> apiUrl
const configInfo = sessionStorage.getItem('configInfo') || '{}'
const apiUrl = JSON.parse(configInfo)?.baseApiUrl || 'http://27.86.34.99/kjapi'
//模糊搜索单位下来数据
export const getUnits = (data) => {
return request({
url: `${
apiUrl}/srbm-bas-mdm-front/member/feignDataRel/org/page`,
method: 'post',
data,
})
}
//二级单位数据
export const getLabelUnits = (data) => {
return request({
url: `${
apiUrl}/srbm-bas-mdm-front/member/feignDataRel/org/search`,
method: 'post',
data,
})
}