feat(cnas-method): 新增方法验证Word表单及上岗证查看功能
| | |
| | | }); |
| | | } |
| | | |
| | | // æå½åæéæ£æµäººå宿¶è¯»åä»»èææè¯ä¹¦ |
| | | export function getCurrentWorkFiles(query) { |
| | | return request({ |
| | | url: '/processMethodVerify/getCurrentWorkFiles', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹æ åæ¹æ³éªè¯ |
| | | export function updateMethodVerify(data) { |
| | | return request({ |
| | |
| | | </el-input> |
| | | </el-form-item > |
| | | <el-form-item label="被任è人å" prop="userId"> |
| | | <el-select v-model="form.userId" placeholder="è¯·éæ©"> |
| | | <el-select v-model="form.userId" filterable placeholder="è¯·éæ©"> |
| | | <el-option |
| | | v-for="(item, index) in userList" |
| | | :key="index" |
| | | v-for="item in userList" |
| | | :key="item.id" |
| | | :label="item.name" |
| | | :value="item.id" |
| | | ></el-option> |
| | |
| | | * @desc è·åç¨æ·ä¿¡æ¯ |
| | | */ |
| | | async getUserList() { |
| | | selectUserCondition({type: 2}).then((res) => { |
| | | this.userList = res.data; |
| | | }) |
| | | try { |
| | | const res = await selectUserCondition() |
| | | if (res.code !== 200) throw new Error('人åå表æ¥å£è¿å失败') |
| | | this.userList = Array.isArray(res.data) ? res.data : [] |
| | | } catch (error) { |
| | | this.userList = [] |
| | | this.$message.error('人åå表å 载失败ï¼è¯·ç¨åéè¯') |
| | | } |
| | | }, |
| | | /** |
| | | * @desc æäº¤è¡¨å |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="filesDialogVisible" title="æ¥çä¸å²è¯" width="80%" @closed="closeFilesLook"> |
| | | <div> |
| | | <el-alert v-if="!tableLoading && tableData.length === 0" type="info" :closable="false" |
| | | title="æéæ£æµäººåææ ä»»èææè®°å½ï¼è¯·æ ¸å¯¹è¢«ä»»è人å䏿£æµäººåæ¯å¦ä¸ºåä¸äººã"/> |
| | | <limsTable |
| | | ref="yearTable" |
| | | :column="columnData" |
| | | :height="'calc(100vh - 400px)'" |
| | | :highlightCurrentRow="true" |
| | | :table-data="tableData" |
| | | :table-loading="tableLoading" |
| | | style="margin-top: 0.5em;"> |
| | | </limsTable> |
| | | </div> |
| | | </el-dialog> |
| | | <el-dialog |
| | | v-model="lookDialogVisible" |
| | | fullscreen |
| | | title="æ¥çéä»¶" top="5vh" width="800px"> |
| | | <filePreview v-if="lookDialogVisible" :currentFile="{}" |
| | | :fileUrl="fileUrl(currentInfo)" style="max-height: 90vh;overflow-y: auto;"/> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import file from '@/utils/file'; |
| | | import filePreview from '@/components/Preview/filePreview.vue'; |
| | | import limsTable from '@/components/Table/lims-table.vue' |
| | | import {getCurrentWorkFiles} from '@/api/cnas/process/method/methodVerification' |
| | | |
| | | export default { |
| | | name: 'viewWorkPermitDia', |
| | | // import å¼å
¥çç»ä»¶éè¦æ³¨å
¥å°å¯¹è±¡ä¸æè½ä½¿ç¨ |
| | | components: {filePreview, limsTable}, |
| | | data() { |
| | | // è¿éåæ¾æ°æ® |
| | | return { |
| | | filesDialogVisible: false, |
| | | tableLoading: false, |
| | | filesLookInfo: {}, |
| | | columnData: [ |
| | | { |
| | | label: 'æä»¶åç§°', |
| | | prop: 'fileName', |
| | | minWidth: '150px' |
| | | }, |
| | | { |
| | | label: 'ææäºº', |
| | | prop: 'userName', |
| | | minWidth: '150px' |
| | | }, |
| | | { |
| | | dataType: 'action', |
| | | minWidth: '100', |
| | | label: 'æä½', |
| | | fixed: 'right', |
| | | operation: [ |
| | | { |
| | | name: 'é¢è§', |
| | | type: 'text', |
| | | clickFun: (row) => { |
| | | this.handleLook(row) |
| | | } |
| | | }, |
| | | { |
| | | name: 'ä¸è½½', |
| | | type: 'text', |
| | | clickFun: (row) => { |
| | | this.upload(row) |
| | | } |
| | | }, |
| | | ] |
| | | } |
| | | ], |
| | | tableData: [], |
| | | info: {}, |
| | | currentInfo: {}, |
| | | lookDialogVisible: false, |
| | | }; |
| | | }, |
| | | mounted() { |
| | | |
| | | }, |
| | | // æ¹æ³éå |
| | | methods: { |
| | | fileUrl(row) { |
| | | const image = /\.(png|jpe?g|gif|bmp|webp)$/i.test(row.fileUrl || '') |
| | | return this.javaApi + (image ? '/img/' : '/word/') + row.fileUrl |
| | | }, |
| | | openDia(userIds) { |
| | | this.filesDialogVisible = true |
| | | this.tableData = [] |
| | | this.tableLoading = true |
| | | getCurrentWorkFiles({userIds: userIds.join(',')}).then(res => { |
| | | if (res.code === 200) this.tableData = res.data || [] |
| | | }).catch(() => { |
| | | this.$message.error('ä¸å²è¯è¯»å失败ï¼è¯·ç¨åéè¯') |
| | | }).finally(() => { |
| | | this.tableLoading = false |
| | | }) |
| | | }, |
| | | closeFilesLook() { |
| | | this.filesDialogVisible = false |
| | | }, |
| | | // æ¥çæä»¶ |
| | | handleLook(row) { |
| | | if (!row.fileUrl) { |
| | | this.$message.warning('该人åå·²æä»»èææè®°å½ï¼ä½å°æªä¸ä¼ ä¸å²è¯ä¹¦æä»¶') |
| | | return |
| | | } |
| | | this.currentInfo = row |
| | | this.lookDialogVisible = true |
| | | }, |
| | | // ä¸è½½ |
| | | upload(row) { |
| | | if (!row.fileUrl) { |
| | | this.$message.warning('该人åå·²æä»»èææè®°å½ï¼ä½å°æªä¸ä¼ ä¸å²è¯ä¹¦æä»¶') |
| | | return |
| | | } |
| | | let url = ''; |
| | | if (/\.(png|jpe?g|gif|bmp|webp)$/i.test(row.fileUrl || '')) { |
| | | url = this.javaApi + '/img/' + row.fileUrl |
| | | file.downloadIamge(url, row.fileName) |
| | | } else { |
| | | url = this.javaApi + '/word/' + row.fileUrl |
| | | const link = document.createElement('a'); |
| | | link.href = url; |
| | | link.download = row.fileName; |
| | | link.click(); |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="visible" :title="operationType === 'edit' ? 'ç¼è¾æ¹æ³éªè¯' : 'æ°å¢æ¹æ³éªè¯'" |
| | | width="90%" :close-on-click-modal="false" @close="closeDia"> |
| | | <div class="form-body"> |
| | | <table class="verify-table"> |
| | | <colgroup> |
| | | <col style="width: 16%"/> |
| | | <col style="width: 36%"/> |
| | | <col style="width: 16%"/> |
| | | <col style="width: 32%"/> |
| | | </colgroup> |
| | | <tbody> |
| | | <tr> |
| | | <th>æ ååç§°</th> |
| | | <td> |
| | | <el-input v-model="form.standardName"/> |
| | | </td> |
| | | <th>æ åç¼å·</th> |
| | | <td> |
| | | <el-input v-model="form.standardCode"/> |
| | | </td> |
| | | </tr> |
| | | <tr> |
| | | <th>éªè¯é¡¹ç®</th> |
| | | <td colspan="3"> |
| | | <el-input v-model="form.verifyItem"/> |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | <table class="verify-table content-table"> |
| | | <colgroup> |
| | | <col style="width: 16%"/> |
| | | <col style="width: 36%"/> |
| | | <col style="width: 36%"/> |
| | | <col style="width: 12%"/> |
| | | </colgroup> |
| | | <thead> |
| | | <tr> |
| | | <th>éªè¯å
容</th> |
| | | <th>æ¹æ³è¦æ±</th> |
| | | <th>å®éªå®¤è½å</th> |
| | | <th>æ¯å¦æ»¡è¶³</th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <tr v-for="(item, index) in content.checks" :key="item.key"> |
| | | <th>{{ index < 7 ? `${index + 1}. ${item.label}` : item.label }}</th> |
| | | <td> |
| | | <el-input v-model="item.requirement" type="textarea" :rows="3"/> |
| | | </td> |
| | | <td> |
| | | <el-input v-model="item.capability" type="textarea" :rows="3"/> |
| | | <div v-if="item.key === 'personnel'" class="row-extra"> |
| | | <span>æ£æµäººå</span> |
| | | <el-select v-model="form.confirmUser" multiple :multiple-limit="5" placeholder="è¯·éæ©æ£æµäººå"> |
| | | <el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id"/> |
| | | </el-select> |
| | | <el-link v-if="operationType === 'edit'" type="primary" @click="viewWorkPermit">æ¥çä¸å²è¯</el-link> |
| | | </div> |
| | | <div v-if="item.key === 'equipment'" class="row-extra"> |
| | | <el-link type="primary" @click="viewDevice">æ¥ç设å¤</el-link> |
| | | <el-link v-if="operationType === 'edit'" type="primary" @click="viewCalibrationsFileDia">æ¥çæ ¡åè¯ä¹¦ |
| | | </el-link> |
| | | </div> |
| | | </td> |
| | | <td class="choice-cell"> |
| | | <el-radio-group v-model="item.satisfied"> |
| | | <el-radio :value="1">æ¯</el-radio> |
| | | <el-radio :value="0">å¦</el-radio> |
| | | </el-radio-group> |
| | | </td> |
| | | </tr> |
| | | <tr> |
| | | <th>éªè¯ç»è®º</th> |
| | | <td colspan="3"> |
| | | <el-input v-model="content.conclusion" type="textarea" :rows="4"/> |
| | | </td> |
| | | </tr> |
| | | <tr v-for="decision in decisions" :key="decision.key"> |
| | | <th>{{ decision.label }}</th> |
| | | <td colspan="3"> |
| | | <div class="decision-row"> |
| | | <el-radio-group v-model="content[decision.key]"> |
| | | <el-radio value="åæä½¿ç¨">åæä½¿ç¨</el-radio> |
| | | <el-radio value="ä¸è½ä½¿ç¨">ä¸è½ä½¿ç¨</el-radio> |
| | | <el-radio value="éå¶ä½¿ç¨">éå¶ä½¿ç¨</el-radio> |
| | | </el-radio-group> |
| | | <span>{{ decision.personLabel }}</span> |
| | | <el-input v-model="content[decision.personKey]" class="person-input"/> |
| | | <span>æ¥æ</span> |
| | | <el-date-picker v-model="content[decision.dateKey]" type="date" value-format="YYYY-MM-DD" |
| | | placeholder="éæ©æ¥æ"/> |
| | | </div> |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | <div v-if="operationType === 'edit'" class="attachments"> |
| | | <span>éä»¶1 åå§è®°å½ãéä»¶2 éªè¯ç»æ</span> |
| | | <el-button size="small" @click="viewTestRecord">æ¥çåä¸ä¼ éä»¶</el-button> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | <el-button :loading="loading" type="primary" @click="submit">æäº¤</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <ViewDeviceDialog v-if="deviceVisible" ref="device" @closDeviceDia="deviceVisible = false" |
| | | @handleDeviceInfo="setDevices"/> |
| | | <ViewTestRecord v-if="recordVisible" ref="record"/> |
| | | <ViewWorkPermitDia v-if="permitVisible" ref="permit"/> |
| | | <CalibrationsFileDia v-if="calibrationVisible" ref="calibration"/> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import ViewDeviceDialog from '../../standardMethodsChange/component/ViewDeviceDialog.vue' |
| | | import ViewTestRecord from '../../standardMethodsChange/component/ViewTestRecord.vue' |
| | | import ViewWorkPermitDia from './viewWorkPermitDia.vue' |
| | | import CalibrationsFileDia from '../../standardMethodsChange/component/calibrationsFileDia.vue' |
| | | import {addMethodVerify, getMethodVerifyOne, updateMethodVerify} from '@/api/cnas/process/method/methodVerification' |
| | | import {selectUserCondition} from '@/api/cnas/process/method/standardMethodsChange' |
| | | |
| | | const labels = [ |
| | | ['personnel', '人åè½å'], ['equipment', 'æ£æµè®¾å¤'], ['reagents', 'è¯åè¦æ±'], |
| | | ['conditions', 'è¯éªæ¡ä»¶'], ['sample', 'æ ·åå¤ç½®'], ['steps', 'è¯éªæ¥éª¤'], |
| | | ['processing', 'æ°æ®å¤ç'], ['qualityControl', 'è´¨éæ§å¶æ¹æ³'], ['result', 'æ¹æ³éªè¯ç»æ'] |
| | | ] |
| | | const decisions = [ |
| | | {key: 'reviewDecision', label: 'è¯å®¡æè§', personKey: 'reviewer', personLabel: 'è¯å®¡äººå', dateKey: 'reviewDate'}, |
| | | { |
| | | key: 'approvalDecision', |
| | | label: 'å®¡æ¹æè§', |
| | | personKey: 'approver', |
| | | personLabel: 'ææ¯è´è´£äºº', |
| | | dateKey: 'approvalDate' |
| | | } |
| | | ] |
| | | |
| | | function emptyContent() { |
| | | return { |
| | | checks: labels.map(([key, label]) => ({key, label, requirement: '', capability: '', satisfied: null})), |
| | | conclusion: '', |
| | | reviewDecision: '', |
| | | reviewer: '', |
| | | reviewDate: '', |
| | | approvalDecision: '', |
| | | approver: '', |
| | | approvalDate: '' |
| | | } |
| | | } |
| | | |
| | | export default { |
| | | name: 'MethodVerificationWordForm', |
| | | components: {ViewDeviceDialog, ViewTestRecord, ViewWorkPermitDia, CalibrationsFileDia}, |
| | | props: {operationType: {type: String, default: 'add'}}, |
| | | data() { |
| | | return { |
| | | visible: false, loading: false, decisions, |
| | | form: {standardName: '', standardCode: '', verifyItem: '', confirmUser: [], machineAttachmentList: []}, |
| | | content: emptyContent(), info: {}, userList: [], |
| | | deviceVisible: false, recordVisible: false, permitVisible: false, calibrationVisible: false |
| | | } |
| | | }, |
| | | methods: { |
| | | openDia(row = {}) { |
| | | this.visible = true |
| | | this.info = row || {} |
| | | selectUserCondition().then(res => { |
| | | if (res.code === 200) this.userList = res.data || [] |
| | | }) |
| | | if (this.operationType === 'edit') this.load(row) |
| | | }, |
| | | load(row) { |
| | | getMethodVerifyOne({methodVerifyId: row.methodVerifyId}).then(res => { |
| | | if (res.code !== 200) return |
| | | this.form = {...res.data, confirmUser: res.data.confirmUser ? res.data.confirmUser.split(',').map(Number) : []} |
| | | try { |
| | | const saved = JSON.parse(res.data.verificationContent || '{}') |
| | | const defaults = emptyContent() |
| | | this.content = { |
| | | ...defaults, ...saved, checks: defaults.checks.map(item => ({ |
| | | ...item, ...(saved.checks || []).find(check => check.key === item.key) |
| | | })) |
| | | } |
| | | } catch (_) { |
| | | this.content = emptyContent() |
| | | this.$message.error('éªè¯å
å®¹æ ¼å¼é误ï¼è¯·è系管çå') |
| | | } |
| | | }) |
| | | }, |
| | | submit() { |
| | | if (!this.form.standardName?.trim() || !this.form.standardCode?.trim() || !this.form.verifyItem?.trim()) { |
| | | this.$message.warning('è¯·å¡«åæ ååç§°ãæ åç¼å·åéªè¯é¡¹ç®') |
| | | return |
| | | } |
| | | this.loading = true |
| | | const payload = { |
| | | ...this.form, operationType: 1, |
| | | confirmUser: (this.form.confirmUser || []).join(','), |
| | | verificationContent: JSON.stringify(this.content) |
| | | } |
| | | const request = this.operationType === 'edit' ? updateMethodVerify : addMethodVerify |
| | | request(payload).then(res => { |
| | | if (res.code === 200) { |
| | | this.$message.success('æä½æå'); |
| | | this.closeDia() |
| | | } |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | openChild(flag, refName, data) { |
| | | this[flag] = true |
| | | this.$nextTick(() => this.$refs[refName]?.openDia(data)) |
| | | }, |
| | | viewWorkPermit() { |
| | | if (!this.form.confirmUser?.length) { |
| | | this.$message.warning('请å
éæ©æ£æµäººå') |
| | | return |
| | | } |
| | | this.openChild('permitVisible', 'permit', this.form.confirmUser) |
| | | }, |
| | | viewDevice() { |
| | | this.openChild('deviceVisible', 'device', this.form) |
| | | }, |
| | | viewCalibrationsFileDia() { |
| | | this.openChild('calibrationVisible', 'calibration', this.form) |
| | | }, |
| | | viewTestRecord() { |
| | | this.openChild('recordVisible', 'record', this.info) |
| | | }, |
| | | setDevices(list) { |
| | | this.deviceVisible = false; |
| | | this.form.machineAttachmentList = list |
| | | }, |
| | | closeDia() { |
| | | this.visible = false; |
| | | this.$emit('closeDia') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .form-body { |
| | | max-height: 70vh; |
| | | overflow: auto; |
| | | } |
| | | |
| | | .verify-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | margin-bottom: 14px; |
| | | table-layout: fixed; |
| | | } |
| | | |
| | | .verify-table th, .verify-table td { |
| | | border: 1px solid #c5cbd0; |
| | | padding: 10px; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | .verify-table th { |
| | | background: #f5f7f8; |
| | | font-weight: 500; |
| | | text-align: center; |
| | | } |
| | | |
| | | .choice-cell { |
| | | text-align: center; |
| | | } |
| | | |
| | | .row-extra, .decision-row, .attachments { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | flex-wrap: wrap; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .person-input { |
| | | width: 140px; |
| | | } |
| | | |
| | | .attachments { |
| | | margin: 16px 0; |
| | | } |
| | | </style> |
| | |
| | | <div style="display: flex;justify-content: space-between"> |
| | | <div style="display: flex;"> |
| | | <div style="margin-bottom: 18px;margin-right: 10px;display: flex;align-items: center;line-height: 32px;"> |
| | | <span style="width: 88px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">æ åæ¹æ³</span> |
| | | <el-input size="small" placeholder="请è¾å
¥" clearable v-model="searchForm.methodName" |
| | | <span style="width: 88px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">æ ååç§°</span> |
| | | <el-input size="small" placeholder="请è¾å
¥" clearable v-model="searchForm.standardName" |
| | | @keyup.enter="searchList"></el-input> |
| | | </div> |
| | | <div style="line-height: 30px;"> |
| | |
| | | @pagination="pagination"> |
| | | </limsTable> |
| | | </div> |
| | | <formDIa v-if="formDIa" ref="formDIa" :operationType="operationType" @closeDia="closeDia"></formDIa> |
| | | <component :is="isWordForm ? 'wordFormDIa' : 'formDIa'" v-if="formDIa" ref="formDIa" |
| | | :operationType="operationType" @closeDia="closeDia" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import limsTable from '@/components/Table/lims-table.vue' |
| | | import formDIa from './component/formDIa.vue'; |
| | | import wordFormDIa from './component/wordFormDIa.vue'; |
| | | import { delMethodVerify, exportMethodVerify, pagesMethodVerify } from '@/api/cnas/process/method/methodVerification' |
| | | |
| | | export default { |
| | | name: 'MethodVerification', |
| | | // import å¼å
¥çç»ä»¶éè¦æ³¨å
¥å°å¯¹è±¡ä¸æè½ä½¿ç¨ |
| | | components: { limsTable, formDIa }, |
| | | components: { limsTable, formDIa, wordFormDIa }, |
| | | data() { |
| | | // è¿éåæ¾æ°æ® |
| | | return { |
| | | searchForm: { |
| | | methodName: '', |
| | | standardName: '', |
| | | operationType: 1, |
| | | }, |
| | | options: [ |
| | |
| | | ], |
| | | tableColumn: [ |
| | | { |
| | | label: 'æ åæ¹æ³', |
| | | prop: 'methodName', |
| | | label: 'æ ååç§°', |
| | | prop: 'standardName', |
| | | minWidth: '100' |
| | | }, |
| | | { |
| | | label: 'éªè¯åå ', |
| | | prop: 'verifyReason', |
| | | label: 'æ åç¼å·', |
| | | prop: 'standardCode', |
| | | minWidth: '100' |
| | | }, |
| | | { |
| | | label: 'ä¸»è¦ææ¯åå', |
| | | prop: 'technologyChange', |
| | | label: 'éªè¯é¡¹ç®', |
| | | prop: 'verifyItem', |
| | | minWidth: '100' |
| | | }, |
| | | { |
| | |
| | | total: 0, |
| | | formDIa: false, |
| | | operationType: '', |
| | | isWordForm: true, |
| | | }; |
| | | }, |
| | | mounted() { |
| | |
| | | // æ¥è¯¢å表 |
| | | searchList() { |
| | | const entity = { |
| | | methodName: this.searchForm.methodName, |
| | | standardName: this.searchForm.standardName, |
| | | operationType: this.searchForm.operationType, |
| | | } |
| | | const page = this.page |
| | |
| | | }, |
| | | // éç½®æ¥è¯¢æ¡ä»¶ |
| | | resetSearchForm() { |
| | | this.searchForm.methodName = ''; |
| | | this.searchForm.standardName = ''; |
| | | this.searchList() |
| | | }, |
| | | openFormDia(type, row) { |
| | | this.formDIa = true |
| | | this.operationType = type |
| | | this.isWordForm = type === 'add' || !!row?.standardCode |
| | | this.$nextTick(() => { |
| | | this.$refs.formDIa.openDia(row) |
| | | }) |
| | |
| | | const env = loadEnv(mode, process.cwd()) |
| | | const { VITE_APP_ENV } = env |
| | | // å端æ¥å£ï¼é»è®¤æå
ç½å
±äº«æå¡ï¼æ¬å°èµ·å端æ¶ç¨ VITE_APP_PROXY_TARGET è¦ç |
| | | const baseUrl = env.VITE_APP_PROXY_TARGET || 'http://192.168.0.24:9509/lims' |
| | | const baseUrl = env.VITE_APP_PROXY_TARGET || 'http://127.0.0.1:9509' |
| | | return { |
| | | // é¨ç½²ç产ç¯å¢åå¼åç¯å¢ä¸çURLã |
| | | // é»è®¤æ
åµä¸ï¼vite ä¼åè®¾ä½ çåºç¨æ¯è¢«é¨ç½²å¨ä¸ä¸ªååçæ ¹è·¯å¾ä¸ |
| | |
| | | server: { |
| | | port: 80, |
| | | host: true, |
| | | allowedHosts: ['.trycloudflare.com'], |
| | | open: true, |
| | | proxy: { |
| | | // https://cn.vitejs.dev/config/#server-proxy |