| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945 |
- <script setup>
- import {
- computed,
- nextTick,
- onMounted,
- ref,
- reactive,
- defineExpose,
- watch,
- } from "vue";
- import { useDataDictStore } from "@/store/modules/dataDict";
- import {
- selectedDeptsEmployee,
- getRuiHenProjectInfoPage,
- getDevicesByDepts,
- getRuiHenTaskDetail1,
- } from "@/api/ruihen";
- import { specifiedSimpleDepts } from "@/api";
- import { getDeptId } from "@/utils/auth";
- import DaTree from "@/components/da-tree/index.vue";
- const props = defineProps({
- type: {
- type: String,
- default: "create",
- },
- id: {
- type: Number,
- default: null,
- },
- });
- const dictStore = useDataDictStore();
- const isReadonly = computed(() => props.type === "detail");
- const createInitialForm = () => ({
- id: "",
- projectId: undefined,
- wellName: "",
- location: "",
- technique: undefined,
- workloadDesign: undefined,
- workloadUnit: undefined,
- deptIds: [],
- deviceIds: [],
- responsiblePerson: undefined,
- remark: "",
- });
- const formRef = ref(null);
- const form = ref(createInitialForm());
- const rules = reactive({
- projectId: {
- rules: [{ required: true, errorMessage: "请选择合同" }],
- },
- wellName: {
- rules: [{ required: true, errorMessage: "请输入井号" }],
- },
- location: {
- rules: [{ required: true, errorMessage: "请输入施工地点" }],
- },
- technique: {
- rules: [{ required: true, errorMessage: "请选择施工工艺" }],
- },
- workloadDesign: {
- rules: [{ required: true, errorMessage: "请输入设计工作量" }],
- },
- workloadUnit: {
- rules: [{ required: true, errorMessage: "请选择工作量单位" }],
- },
- deptIds: {
- rules: [{ required: true, errorMessage: "请选择施工队伍" }],
- },
- deviceIds: {
- rules: [{ required: true, errorMessage: "请选择施工设备" }],
- },
- responsiblePerson: {
- rules: [{ required: true, errorMessage: "请选择责任人" }],
- },
- });
- const validate = async () => {
- return await formRef.value?.validate();
- };
- const buildSubmitData = () => {
- return {
- ...(form.value.id ? { id: form.value.id } : {}),
- projectId: form.value.projectId,
- wellName: form.value.wellName,
- location: form.value.location,
- technique: form.value.technique,
- workloadDesign: form.value.workloadDesign,
- workloadUnit: form.value.workloadUnit,
- deptIds: form.value.deptIds,
- deviceIds: form.value.deviceIds,
- responsiblePerson: [form.value.responsiblePerson],
- remark: form.value.remark,
- platformWell: "0",
- };
- };
- const defaultProps = computed(() => (label) => ({
- inputBorder: false,
- clearable: false,
- placeholder: isReadonly.value ? " " : `请输入${label}`,
- style: {
- "text-align": "right",
- },
- styles: {
- disableColor: "#fff",
- },
- }));
- const contractPopup = ref(null);
- const contractPaging = ref(null);
- const contractList = ref([]);
- const contractSearchValue = ref("");
- const contractTempProjectId = ref("");
- const selectedContractLabel = ref("");
- const contractSearchInputStyles = reactive({
- backgroundColor: "#f5f5f5",
- color: "#333",
- });
- const contractDisplayText = computed(() => {
- if (!form.value.projectId) return "";
- const currentContract = contractList.value.find(
- (item) => String(item.id) === String(form.value.projectId)
- );
- return selectedContractLabel.value || currentContract?.contractName || "";
- });
- const syncSelectedContractLabel = (list = []) => {
- if (!form.value.projectId) return;
- const currentContract = list.find(
- (item) => String(item.id) === String(form.value.projectId)
- );
- if (currentContract?.contractName) {
- selectedContractLabel.value = currentContract.contractName;
- }
- };
- const queryContractList = async (pageNo, pageSize) => {
- try {
- const res = await getRuiHenProjectInfoPage({
- deptId: getDeptId(),
- pageNo,
- pageSize,
- ...(contractSearchValue.value
- ? { contractName: contractSearchValue.value }
- : {}),
- });
- const list = res?.data?.list || [];
- const total = res?.data?.total || 0;
- syncSelectedContractLabel(list);
- contractPaging.value?.completeByTotal(list, total);
- } catch (error) {
- contractPaging.value?.complete(false);
- }
- };
- const searchContractList = () => {
- contractPaging.value?.reload();
- };
- const openContractPopup = () => {
- contractSearchValue.value = "";
- contractTempProjectId.value = form.value.projectId
- ? String(form.value.projectId)
- : "";
- contractPopup.value?.open();
- nextTick(() => {
- contractPaging.value?.reload();
- });
- };
- const closeContractPopup = () => {
- contractPopup.value?.close();
- };
- const handleContractRadioChange = (event) => {
- contractTempProjectId.value = event.detail.value;
- };
- const handleContractRowClick = (item) => {
- contractTempProjectId.value = String(item.id);
- };
- const handleContractConfirm = () => {
- if (!contractTempProjectId.value) {
- uni.showToast({
- title: "请选择合同",
- icon: "none",
- });
- return;
- }
- const currentContract = contractList.value.find(
- (item) => String(item.id) === contractTempProjectId.value
- );
- const fallbackProjectId = Number(contractTempProjectId.value);
- form.value.projectId =
- currentContract?.id ??
- (Number.isNaN(fallbackProjectId)
- ? contractTempProjectId.value
- : fallbackProjectId);
- selectedContractLabel.value =
- currentContract?.contractName || selectedContractLabel.value;
- closeContractPopup();
- };
- const techniqueOptions = ref([]);
- const workloadUnitOptions = ref([]);
- const loadDictOptions = async () => {
- if (dictStore.dataDict.length <= 0) {
- await dictStore.loadDataDictList();
- }
- techniqueOptions.value = dictStore
- .getIntDictOptions("rq_iot_project_technology_rh")
- .map((item) => ({
- text: item.label,
- value: item.value,
- }));
- workloadUnitOptions.value = dictStore
- .getIntDictOptions("rq_iot_project_measure_unit")
- .map((item) => ({
- text: item.label,
- value: item.value,
- }));
- };
- const deptOptions = ref([]);
- const treeData = ref([]);
- const deptLoading = ref(false);
- const handleTree = (data, id, parentId, children) => {
- if (!Array.isArray(data)) {
- console.warn("data must be an array");
- return [];
- }
- const config = {
- id: id || "id",
- parentId: parentId || "parentId",
- childrenList: children || "children",
- };
- const childrenListMap = {};
- const nodeIds = {};
- const tree = [];
- for (const d of data) {
- const parentId = d[config.parentId];
- if (childrenListMap[parentId] == null) {
- childrenListMap[parentId] = [];
- }
- nodeIds[d[config.id]] = d;
- childrenListMap[parentId].push(d);
- }
- for (const d of data) {
- const parentId = d[config.parentId];
- if (nodeIds[parentId] == null) {
- tree.push(d);
- }
- }
- for (const t of tree) {
- adaptToChildrenList(t);
- }
- function adaptToChildrenList(o) {
- if (childrenListMap[o[config.id]] !== null) {
- o[config.childrenList] = childrenListMap[o[config.id]];
- }
- if (o[config.childrenList]) {
- for (const c of o[config.childrenList]) {
- adaptToChildrenList(c);
- }
- }
- }
- return tree;
- };
- async function loadDeptOptions() {
- deptLoading.value = true;
- try {
- function sortTreeBySort(treeNodes) {
- if (!treeNodes || !Array.isArray(treeNodes)) return treeNodes;
- const sortedNodes = [...treeNodes].sort((a, b) => {
- const sortA = a.sort != null ? a.sort : 999999;
- const sortB = b.sort != null ? b.sort : 999999;
- return sortA - sortB;
- });
- sortedNodes.forEach((node) => {
- node.disabled = node.type !== "3";
- if (node.children && Array.isArray(node.children)) {
- node.children = sortTreeBySort(node.children);
- }
- });
- return sortedNodes;
- }
- const depts = await specifiedSimpleDepts(getDeptId());
- deptOptions.value = depts.data.map((item) => ({
- text: item.name,
- value: item.id,
- raw: item,
- }));
- treeData.value = sortTreeBySort(handleTree(depts.data));
- } catch (error) {
- } finally {
- deptLoading.value = false;
- }
- }
- const popup = ref();
- const openPopup = () => {
- popup.value.open();
- };
- const selectDeptId = ref("");
- function handleTreeChange(values) {
- selectDeptId.value = values;
- }
- function handleConfirm() {
- form.value.deptIds = [selectDeptId.value];
- popup.value.close();
- loadDeviceOptions();
- loadUserOptions();
- }
- const deviceOptions = ref([]);
- const deviceLoading = ref(false);
- const loadDeviceOptions = async (init = false) => {
- deviceOptions.value = [];
- if (!init) form.value.deviceIds = [];
- if (!form.value.deptIds || form.value.deptIds.length === 0) {
- return;
- }
- deviceLoading.value = true;
- try {
- const res = await getDevicesByDepts({ deptIds: form.value.deptIds });
- deviceOptions.value = res.data.map((item) => ({
- text: `${item.deviceCode} ${item.deviceName}`,
- value: item.id,
- raw: item,
- }));
- if (!init) {
- form.value.deviceIds = deviceOptions.value.map((item) => item.value);
- }
- } finally {
- deviceLoading.value = false;
- }
- };
- const userOptions = ref([]);
- const userLoading = ref(false);
- const loadUserOptions = async (init = false) => {
- userOptions.value = [];
- if (!init) form.value.responsiblePerson = undefined;
- if (!form.value.deptIds || form.value.deptIds.length === 0) {
- return;
- }
- userLoading.value = true;
- try {
- const res = await selectedDeptsEmployee({
- deptIds: form.value.deptIds,
- });
- userOptions.value = res.data.map((item) => ({
- text: item.nickname,
- value: item.id,
- raw: item,
- }));
- } finally {
- userLoading.value = false;
- }
- };
- const loadDetail = async (id) => {
- if (!id) return;
- const res = await getRuiHenTaskDetail1({ id });
- const data = res.data.list[0] || createInitialForm();
- Object.assign(form.value, {
- id: data.id,
- projectId: data.projectId,
- wellName: data.wellName,
- location: data.location,
- technique: Number(data.technique),
- workloadDesign: data.workloadDesign,
- workloadUnit: Number(data.workloadUnit),
- deptIds: data.deptIds || [],
- deviceIds: data.deviceIds || [],
- responsiblePerson: data.responsiblePerson
- ? data.responsiblePerson[0]
- : undefined,
- remark: data.remark,
- });
- selectedContractLabel.value = data.contractName || "";
- loadDeviceOptions(true);
- loadUserOptions(true);
- };
- watch(
- () => props.id,
- async (newId) => {
- if (props.type !== "create" && newId) {
- await loadDetail(newId);
- }
- },
- { immediate: true }
- );
- watch(
- () => form.value.projectId,
- (value) => {
- if (!value) {
- selectedContractLabel.value = "";
- contractTempProjectId.value = "";
- }
- }
- );
- onMounted(async () => {
- await Promise.all([loadDictOptions(), loadDeptOptions()]);
- });
- defineExpose({
- form,
- formRef,
- validate,
- buildSubmitData,
- });
- </script>
- <template>
- <view class="content">
- <uni-forms
- ref="formRef"
- labelWidth="auto"
- :model="form"
- :rules="rules"
- validateTrigger="submit"
- err-show-type="toast">
- <uni-forms-item label="合同" name="projectId" required>
- <view class="select-with-button">
- <view
- class="popup-select-value"
- :class="{ 'popup-select-placeholder': !contractDisplayText }"
- @click="!isReadonly && openContractPopup()">
- {{ contractDisplayText || "请选择合同" }}
- </view>
- <button
- v-if="!isReadonly"
- class="popup-button"
- type="primary"
- size="mini"
- @click="openContractPopup">
- 选择
- </button>
- </view>
- </uni-forms-item>
- <uni-forms-item label="井号" name="wellName" required>
- <uni-easyinput
- v-bind="defaultProps('井号')"
- v-model="form.wellName"
- :disabled="isReadonly" />
- </uni-forms-item>
- <uni-forms-item label="施工地点" name="location" required>
- <uni-easyinput
- v-bind="defaultProps('施工地点')"
- v-model="form.location"
- :disabled="isReadonly" />
- </uni-forms-item>
- <uni-forms-item label="施工工艺" name="technique" required>
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择施工工艺"
- :localdata="techniqueOptions"
- placement="bottom"
- hideRight
- :disabled="isReadonly"
- v-model="form.technique" />
- </uni-forms-item>
- <uni-forms-item label="设计工作量" name="workloadDesign" required>
- <uni-easyinput
- type="number"
- v-bind="defaultProps('设计工作量')"
- v-model.number="form.workloadDesign"
- :disabled="isReadonly" />
- </uni-forms-item>
- <uni-forms-item label="工作量单位" name="workloadUnit" required>
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择工作量单位"
- :localdata="workloadUnitOptions"
- placement="bottom"
- hideRight
- :disabled="isReadonly"
- v-model="form.workloadUnit" />
- </uni-forms-item>
- <uni-forms-item label="施工队伍" name="deptIds" required>
- <view class="select-with-button">
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择施工队伍"
- :localdata="deptOptions"
- placement="bottom"
- hideRight
- :disabled="true"
- multiple
- v-model="form.deptIds" />
- <button
- v-if="!isReadonly"
- class="popup-button"
- type="primary"
- size="mini"
- :disabled="deptLoading"
- @click="openPopup">
- 选择
- </button>
- </view>
- </uni-forms-item>
- <uni-forms-item label="施工设备" name="deviceIds" required>
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择施工设备"
- :localdata="deviceOptions"
- placement="top"
- hideRight
- multiple
- :disabled="isReadonly || deviceLoading"
- v-model="form.deviceIds">
- <template #selected="{ selectedItems }">
- <view class="device-selected-box">
- <view v-if="selectedItems.length" class="device-selected-list">
- <view
- v-for="item in selectedItems"
- :key="item.value"
- class="device-selected-item">
- {{ item.text }}
- </view>
- </view>
- <view v-else class="device-selected-placeholder">
- 请选择施工设备
- </view>
- </view>
- </template>
- </uni-data-select>
- </uni-forms-item>
- <uni-forms-item label="责任人" name="responsiblePerson" required>
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择责任人"
- :localdata="userOptions"
- placement="bottom"
- hideRight
- :disabled="isReadonly || userLoading"
- v-model="form.responsiblePerson" />
- </uni-forms-item>
- <uni-forms-item label="备注" name="remark">
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps('备注')"
- v-model="form.remark"
- :disabled="isReadonly"
- :maxlength="1000" />
- </uni-forms-item>
- </uni-forms>
- </view>
- <uni-popup
- ref="contractPopup"
- type="bottom"
- :is-mask-click="false"
- border-radius="10px 10px 0 0">
- <z-paging
- ref="contractPaging"
- v-model="contractList"
- class="contract-popup-paging"
- style="top: 140px"
- :default-page-size="20"
- @query="queryContractList">
- <template #top>
- <view class="contract-popup-top">
- <view class="contract-popup-header">
- <text class="contract-popup-action" @click="closeContractPopup">
- 取消
- </text>
- <text class="contract-popup-title">选择合同</text>
- <text
- class="contract-popup-action primary"
- @click="handleContractConfirm">
- 确定
- </text>
- </view>
- <view class="contract-search-row">
- <uni-easyinput
- v-model="contractSearchValue"
- :inputBorder="false"
- :styles="contractSearchInputStyles"
- placeholder="请输入合同名称"
- @confirm="searchContractList" />
- <button
- class="mini-btn contract-search-button"
- type="primary"
- size="mini"
- @click="searchContractList">
- 搜索
- </button>
- </view>
- </view>
- </template>
- <radio-group @change="handleContractRadioChange">
- <view class="contract-popup-list">
- <view
- v-for="item in contractList"
- :key="item.id"
- class="contract-popup-item"
- :class="{ active: String(item.id) === contractTempProjectId }"
- @click="handleContractRowClick(item)">
- <view class="contract-popup-item-main">
- <view class="contract-popup-item-name">
- {{ item.contractName || "--" }}
- </view>
- <!-- <view
- v-if="item.projectNo || item.contractCode"
- class="contract-popup-item-desc">
- {{ item.projectNo || item.contractCode }}
- </view> -->
- </view>
- <radio
- :value="String(item.id)"
- :checked="String(item.id) === contractTempProjectId"
- color="#2979ff" />
- </view>
- </view>
- </radio-group>
- </z-paging>
- </uni-popup>
- <uni-popup ref="popup" type="bottom">
- <view class="popup-content">
- <view class="tree">
- <DaTree
- :data="treeData"
- labelField="name"
- valueField="id"
- disabledField="disabled"
- defaultExpandAll
- checkedDisabled
- :defaultCheckedKeys="form.deptIds[0]"
- @change="handleTreeChange"></DaTree>
- </view>
- <button class="mini-btn" type="primary" @click="handleConfirm">
- 确定
- </button>
- </view>
- </uni-popup>
- </template>
- <style lang="scss" scoped>
- .content {
- background-color: #fff;
- padding: 16px;
- border-radius: 8px;
- box-sizing: border-box;
- }
- .uni-forms {
- margin-top: 10px;
- }
- :deep(.uni-forms-item) {
- display: flex;
- align-items: center;
- margin-bottom: 6px;
- border-bottom: 1px dashed #cacccf;
- }
- :deep(.uni-forms-item__content) {
- text-align: right;
- }
- :deep(.uni-forms-item__label) {
- height: 44px;
- font-weight: 500;
- font-size: 14px;
- color: #333 !important;
- width: max-content !important;
- }
- :deep(.uni-select) {
- border: none;
- text-align: right;
- padding-right: 10px;
- }
- :deep(.uniui-bottom:before) {
- content: "\e6b5" !important;
- font-size: 16px !important;
- }
- :deep(.uni-easyinput__content-textarea) {
- min-height: inherit;
- margin: 10px;
- }
- :deep(.uni-textarea-textarea:disabled),
- :deep(.uni-input-input:disabled),
- :deep(.is-disabled) {
- color: #333 !important;
- }
- :deep(.uni-select--disabled) {
- background-color: #fff;
- }
- .select-with-button {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- gap: 10px;
- width: 100%;
- }
- .readonly-input {
- flex: 1;
- }
- .popup-select-value {
- flex: 1;
- min-height: 32px;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- color: #333;
- font-size: 14px;
- line-height: 1.4;
- text-align: right;
- word-break: break-all;
- }
- .popup-select-placeholder {
- color: #999;
- }
- .contract-popup-paging {
- background-color: #fff;
- border-radius: 10px 10px 0 0;
- }
- .contract-popup-top {
- padding: 16px;
- padding-bottom: 10px;
- background-color: #fff;
- }
- .contract-popup-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 16px;
- font-size: 15px;
- }
- .contract-popup-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- }
- .contract-popup-action {
- color: #666;
- }
- .contract-popup-action.primary {
- color: #2979ff;
- }
- .contract-search-row {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .contract-search-button {
- width: 72px;
- margin: 0;
- flex-shrink: 0;
- }
- .contract-popup-list {
- padding: 0 16px 16px;
- background-color: #fff;
- }
- .contract-popup-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
- padding: 14px 0;
- border-bottom: 1px solid #f0f0f0;
- }
- .contract-popup-item.active {
- color: #2979ff;
- }
- .contract-popup-item-main {
- flex: 1;
- min-width: 0;
- }
- .contract-popup-item-name {
- font-size: 14px;
- line-height: 1.5;
- color: inherit;
- word-break: break-all;
- }
- .contract-popup-item-desc {
- margin-top: 4px;
- font-size: 12px;
- color: #999;
- }
- .device-selected-box {
- width: 100%;
- min-height: 24px;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- }
- .device-selected-list {
- width: 100%;
- display: flex;
- flex-wrap: wrap;
- justify-content: flex-end;
- gap: 6px;
- }
- .device-selected-item {
- max-width: 100%;
- padding: 4px 8px;
- border-radius: 4px;
- background: #f3f5f9;
- color: #333;
- font-size: 12px;
- line-height: 1.4;
- word-break: break-all;
- }
- .device-selected-placeholder {
- color: #999;
- font-size: 14px;
- }
- :deep(.popup-button) {
- width: 62px;
- margin: 0;
- flex-shrink: 0;
- }
- .popup-content {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- padding: 15px;
- height: 500px;
- background-color: #fff;
- }
- .tree {
- flex: 1;
- max-height: 420px;
- }
- .popup-content button {
- width: 100%;
- height: 44px;
- }
- :deep(.uni-select__selector-item) {
- padding: 0;
- width: 100%;
- overflow-x: auto;
- overflow-y: hidden;
- white-space: nowrap;
- -webkit-overflow-scrolling: touch;
- uni-text {
- padding: 0 10px;
- }
- }
- :deep(.uni-select__selector-item uni-text),
- :deep(.uni-select__selector-item uni-text span) {
- display: inline-block;
- white-space: nowrap;
- min-width: max-content;
- }
- </style>
|