sampleAppoint.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <!--
  2. * @Author: wanglj wanglijie@dashoo.cn
  3. * @Date: 2025-03-24 09:17:15
  4. * @LastEditors: wanglj wanglijie@dashoo.cn
  5. * @LastEditTime: 2025-03-28 11:44:38
  6. * @FilePath: \labsop_h5\src\view\instr\detail.vue
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. -->
  9. <template>
  10. <div class="container">
  11. <van-form ref="formRef" required="auto">
  12. <van-cell-group>
  13. <van-field v-model="state.form.deliverTime" is-link readonly label="送样时间" placeholder="请选择计划送样时间"
  14. @click="state.showDeliverTime = true" :rules="[{ required: true, message: '送样时间不能为空' }]" />
  15. <van-field v-model="state.form.testTime" is-link readonly label="检测时间" placeholder="请选择检测时间"
  16. @click="state.showTestTime = true" :rules="[{ required: true, message: '检测时间不能为空' }]" />
  17. <van-field label="课题" placeholder="课题" readonly v-model="state.form.projectName"
  18. :rules="[{ required: false }]" />
  19. <van-field v-if="state.InstCfgCharge" label="经费卡" placeholder="请选择经费卡" is-link readonly
  20. @click="state.showExpenseCard = true" v-model="state.form.expenseCardName"
  21. :rules="[{ required: isRequiredExpense, message: '经费卡不能为空' }]"></van-field>
  22. <van-field label="样品说明" placeholder="输入样品说明" v-model="state.form.sampleDesc"
  23. :rules="[{ required: false }]"></van-field>
  24. <van-field label="联系电话" placeholder="输入联系电话" v-model="state.form.userContact"
  25. :rules="[{ required: false }]"></van-field>
  26. </van-cell-group>
  27. <div class="mt10 card-wrap" v-if="state.form.sampleItem.length > 0">
  28. <h4>检测信息</h4>
  29. <div class="card-item" v-for="(v, i) in state.form.sampleItem" :key="i">
  30. <div class="flex flex-between mb10">
  31. <span class="label">检测项:</span>
  32. <span class="value bold">{{ v.name }}</span>
  33. </div>
  34. <div class="flex flex-between mb10 flex-center">
  35. <span class="label">样本数量:</span>
  36. <van-stepper v-model="v.count" min="0" integer />
  37. </div>
  38. <div class="flex flex-between mb10">
  39. <span class="label">单价:</span>
  40. <span class="value price">¥{{ v.price }}</span>
  41. </div>
  42. <div class="flex flex-between">
  43. <span class="label">总金额:</span>
  44. <span class="value price bold">¥{{ (v.count * v.price).toFixed(2) }}</span>
  45. </div>
  46. </div>
  47. </div>
  48. <CustomForm ref="customFormRef" :formData="state.form.sampleForm"></CustomForm>
  49. </van-form>
  50. </div>
  51. <van-action-bar placeholder>
  52. <van-action-bar-button class="w100" type="primary" text="保存" @click="onClickButton" :loading="state.loading" />
  53. </van-action-bar>
  54. <!-- 送样时间 -->
  55. <van-popup v-model:show="state.showDeliverTime" position="bottom">
  56. <van-date-picker v-model="state.currentDate" title="选择送样时间" @confirm="onConfirmDeliverDate"
  57. @cancel="state.showDeliverTime = false" />
  58. </van-popup>
  59. <van-popup v-model:show="state.showDeliverTimeTime" position="bottom">
  60. <van-time-picker v-model="state.currentTime" title="选择送样时间" @confirm="onConfirmDeliverTime"
  61. @cancel="state.showDeliverTimeTime = false" />
  62. </van-popup>
  63. <!-- 检测时间 -->
  64. <van-popup v-model:show="state.showTestTime" position="bottom">
  65. <van-date-picker v-model="state.currentDateTest" title="选择检测时间" @confirm="onConfirmTestDate"
  66. @cancel="state.showTestTime = false" />
  67. </van-popup>
  68. <van-popup v-model:show="state.showTestTimeTime" position="bottom">
  69. <van-time-picker v-model="state.currentTimeTest" title="选择检测时间" @confirm="onConfirmTestTime"
  70. @cancel="state.showTestTimeTime = false" />
  71. </van-popup>
  72. <!-- 选择经费卡 -->
  73. <van-popup v-model:show="state.showExpenseCard" position="bottom">
  74. <van-picker :columns="fundsList" :columns-field-names="{ text: 'finAccount', value: 'id' }"
  75. @confirm="pickExpenseCard" @cancel="state.showExpenseCard = false" />
  76. </van-popup>
  77. </template>
  78. <script lang="ts" setup>
  79. import to from 'await-to-js'
  80. import { useRoute, useRouter } from 'vue-router'
  81. import { useInstrApi } from '/@/api/instr'
  82. import { defineAsyncComponent, onMounted, reactive, ref, computed } from 'vue'
  83. import { formatDate } from '/@/utils/formatTime'
  84. import { showNotify, showToast } from 'vant'
  85. import { useProApi } from '/@/api/project'
  86. import { useSampleApi } from '/@/api/instr/sample'
  87. const CustomForm = defineAsyncComponent(() => import('/@/components/CustomForm.vue'))
  88. const route = useRoute()
  89. const router = useRouter()
  90. const projApi = useProApi()
  91. const instApi = useInstrApi()
  92. const sampleApi = useSampleApi()
  93. const fundsList = ref([])
  94. const formRef = ref()
  95. const customFormRef = ref()
  96. const state = reactive({
  97. loading: false,
  98. InstCfgCharge: false,
  99. showDeliverTime: false,
  100. showDeliverTimeTime: false,
  101. currentDate: [],
  102. currentTime: [],
  103. showTestTime: false,
  104. showTestTimeTime: false,
  105. currentDateTest: [],
  106. currentTimeTest: [],
  107. showExpenseCard: false,
  108. form: {
  109. instId: 0,
  110. deliverTime: '',
  111. testTime: '',
  112. projectName: '',
  113. projectId: null,
  114. expenseCardId: 0,
  115. expenseCardName: '',
  116. sampleDesc: '',
  117. sampleItem: [] as any[],
  118. userContact: '',
  119. sampleForm: [] as any[],
  120. },
  121. })
  122. const isRequiredExpense = computed(() => state.InstCfgCharge && state.form.sampleItem && state.form.sampleItem.length > 0)
  123. // Determine Default Time
  124. const now = new Date()
  125. const dateStr = formatDate(now, 'YYYY-mm-dd')
  126. const timeStr = formatDate(now, 'HH:MM')
  127. state.currentDate = dateStr.split('-')
  128. state.currentTime = timeStr.split(':')
  129. state.currentDateTest = dateStr.split('-')
  130. state.currentTimeTest = timeStr.split(':')
  131. state.form.deliverTime = ''
  132. state.form.testTime = ''
  133. const onConfirmDeliverDate = ({ selectedValues }) => {
  134. state.currentDate = selectedValues
  135. state.showDeliverTime = false
  136. state.showDeliverTimeTime = true
  137. }
  138. const onConfirmDeliverTime = ({ selectedValues }) => {
  139. state.currentTime = selectedValues
  140. state.form.deliverTime = `${state.currentDate.join('-')} ${state.currentTime.join(':')}`
  141. state.showDeliverTimeTime = false
  142. }
  143. const onConfirmTestDate = ({ selectedValues }) => {
  144. state.currentDateTest = selectedValues
  145. state.showTestTime = false
  146. state.showTestTimeTime = true
  147. }
  148. const onConfirmTestTime = ({ selectedValues }) => {
  149. state.currentTimeTest = selectedValues
  150. state.form.testTime = `${state.currentDateTest.join('-')} ${state.currentTimeTest.join(':')}`
  151. state.showTestTimeTime = false
  152. }
  153. const init = async () => {
  154. getSampleConfig()
  155. getChargeConfig()
  156. }
  157. // 送样配置信息
  158. const getSampleConfig = async () => {
  159. const params = {
  160. instId: state.form.instId,
  161. code: 'InstCfgSample',
  162. }
  163. const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
  164. if (err) return
  165. state.form.sampleForm = res?.data?.config.sampleForm ? JSON.parse(res.data.config.sampleForm) : []
  166. }
  167. // 计费配置信息
  168. const getChargeConfig = async () => {
  169. const params = {
  170. instId: state.form.instId,
  171. code: 'InstCfgCharge',
  172. }
  173. const [err, res]: ToResponse = await to(instApi.getSettingDetail({ ...params }))
  174. if (err) return
  175. state.InstCfgCharge = res?.data.config.enable && !res?.data.config.sampleFreeEnable
  176. if (res?.data?.config?.sampleCountEnable && res?.data?.config?.enable) {
  177. state.form.sampleItem = res?.data?.config?.sampleItemPrice || []
  178. state.form.sampleItem = state.form.sampleItem.map((item) => {
  179. return {
  180. ...item,
  181. count: 1,
  182. }
  183. })
  184. }
  185. await getMyProjectInfo()
  186. }
  187. const getMyProjectInfo = async () => {
  188. const [err, res]: ToResponse = await to(projApi.getMySelfProjectGroup({}))
  189. if (err) return
  190. state.form.projectName = res?.data.pgName || ''
  191. state.form.projectId = res?.data.id || null
  192. if (state.form.projectId) {
  193. getFundsData()
  194. getTestList()
  195. }
  196. }
  197. const getFundsData = async () => {
  198. const [err, res]: ToResponse = await to(projApi.getFinanceAccountList({ projId: state.form.projectId }))
  199. if (err) return
  200. fundsList.value = res?.data.list ? [res?.data.list] : []
  201. if (fundsList.value && fundsList.value.length > 0 && fundsList.value[0].length > 0) {
  202. state.form.expenseCardId = fundsList.value[0][0].id
  203. state.form.expenseCardName = fundsList.value[0][0].finAccount
  204. }
  205. }
  206. const getTestList = async () => {
  207. const [err, res]: ToResponse = await to(
  208. sampleApi.getSampleTestOption({
  209. instId: state.form.instId,
  210. projectId: state.form.projectId,
  211. }),
  212. )
  213. if (err) return
  214. if (res.data.length > 0) {
  215. state.form.sampleItem.forEach((item) => {
  216. item.price = res.data.find((price) => price.name === item.name)?.price || 0
  217. })
  218. }
  219. }
  220. // 经费卡选择
  221. const pickExpenseCard = ({ selectedOptions }) => {
  222. state.form.expenseCardId = selectedOptions[0].id
  223. state.form.expenseCardName = selectedOptions[0].finAccount
  224. state.showExpenseCard = false
  225. }
  226. const onClickButton = async () => {
  227. state.loading = true
  228. const [errValid] = await to(formRef.value.validate())
  229. if (errValid) {
  230. state.loading = false
  231. return
  232. }
  233. if (isRequiredExpense.value && !state.form.expenseCardId) {
  234. showNotify({ type: 'warning', message: '请选择经费卡' })
  235. state.loading = false
  236. return
  237. }
  238. const customForm = customFormRef.value.getFormData()
  239. if (state.form.sampleForm.length > 0 && !customForm) {
  240. state.loading = false
  241. return
  242. }
  243. const sampleItem = state.form.sampleItem.map((item) => {
  244. return {
  245. name: item.name,
  246. count: Number(item.count),
  247. }
  248. })
  249. let params: any = Object.assign({}, state.form)
  250. params.sampleItem = sampleItem
  251. params.sampleForm = JSON.stringify(customForm || [])
  252. const [err, res]: ToResponse = await to(sampleApi.add(params))
  253. state.loading = false
  254. if (err) return
  255. if (res && res.code == 200) {
  256. showToast({
  257. type: 'success',
  258. message: '提交成功'
  259. })
  260. router.back()
  261. }
  262. }
  263. onMounted(() => {
  264. state.form.instId = route.query.id ? +route.query.id : 0
  265. init()
  266. })
  267. </script>
  268. <style lang="scss" scoped>
  269. .container {
  270. flex: 1;
  271. padding: 10px;
  272. background-color: #f7f8fa;
  273. overflow-y: auto;
  274. h4 {
  275. height: 18px;
  276. line-height: 18px;
  277. display: flex;
  278. margin: 10px 0;
  279. span {
  280. font-weight: normal;
  281. margin-left: auto;
  282. }
  283. &::before {
  284. display: inline-block;
  285. content: '';
  286. width: 3px;
  287. height: 18px;
  288. background-color: #1c9bfd;
  289. margin-right: 4px;
  290. vertical-align: middle;
  291. }
  292. }
  293. }
  294. .card-wrap {
  295. padding-bottom: 20px;
  296. }
  297. .card-item {
  298. background: #fff;
  299. padding: 15px;
  300. border-radius: 8px;
  301. margin-bottom: 10px;
  302. box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
  303. .label {
  304. color: #646566;
  305. font-size: 14px;
  306. }
  307. .value {
  308. color: #323233;
  309. font-size: 14px;
  310. &.bold {
  311. font-weight: bold;
  312. }
  313. &.price {
  314. color: #ee0a24;
  315. }
  316. }
  317. }
  318. .flex {
  319. display: flex;
  320. }
  321. .flex-between {
  322. justify-content: space-between;
  323. }
  324. .flex-center {
  325. align-items: center;
  326. }
  327. .mb10 {
  328. margin-bottom: 10px;
  329. }
  330. </style>