Quellcode durchsuchen

task-245 业务管理/药品目录管理 重构映射管理

cc12458 vor 6 Monaten
Ursprung
Commit
3dd993e31d
3 geänderte Dateien mit 303 neuen und 103 gelöschten Zeilen
  1. 25 1
      src/api/business.js
  2. 4 5
      src/views/business/DrugList.vue
  3. 274 97
      src/views/business/DrugMapList.vue

+ 25 - 1
src/api/business.js

@@ -242,7 +242,31 @@ export function addOrUpdateDrug(data) {
         data
     })
 };
-
+export function getDrugMap(pid) {
+    const group = (array, level = 0) => {
+        if (!Array.isArray(array) || !array.length) return level > 1 ? void 0 : [];
+        return array.map(item => {
+            const children = group(item.children, level + 1);
+            return children ? {
+                value: item.pid,
+                label: item.name,
+                children, leaf: false,
+            } : {
+                value: item.pid,
+                label: item.name,
+                leaf: true,
+            };
+        });
+    };
+    return request({url: `/basis/stitutionsdrugMgr/getDrugCatalogueInfo/${pid}`, method: 'get'}).then((res) => {
+        if (res.ResultCode !== 0) throw {message: res.ResultInfo};
+        const data = res.Data;
+        return {
+            pid: data.pid,
+            groupInfo: group(JSON.parse(data.groupInfo)),
+        };
+    });
+}
 // 获取药品映射详情
 export function getDrugMapList(data) {
     return request({

+ 4 - 5
src/views/business/DrugList.vue

@@ -62,11 +62,7 @@
                 <!-- <div class="flex-center" style="margin-right:20px;" v-if="showAdd" @click="add(scope)">
                 新增</div>-->
                 <div class="flex-center" v-if="showEdit" @click="openEditDialog(scope.row)">修改</div>
-                <div
-                  class="flex-center bg-yellow"
-                  v-if="showMap"
-                  @click="$router.push({path:'/index/drugmaplist?id='+scope.row.ygtid})"
-                >映射管理</div>
+                <div class="flex-center bg-yellow" v-if="showMap" @click="openMapPage(scope.row)">映射管理</div>
                 <div class="flex-center bg-red" v-if="showMapAuto" @click="autoMap(scope)">自动映射</div>
                 <div class="flex-center bg-yellow" v-if="showImport">
                   导入
@@ -259,6 +255,9 @@ export default {
         }, () => void 0);
       }
     },
+    openMapPage(row) {
+      this.$router.push({path: `/index/drugmaplist?pid=${row.pid}`});
+    },
     async openEditDialog(row = {}) {
       this.editData = {
         pid: row.pid,

+ 274 - 97
src/views/business/DrugMapList.vue

@@ -6,30 +6,57 @@
         <img src="~@/assets/filters.png" alt />
       </div>
       <div class="screening-form flex-vertical-center-l flex-wrap">
+        <div class="screening-item flex-vertical-center-l">
+          <span>医共体名称:</span>
+          <div class="input">
+            <el-select size="mini" placeholder="请选择" :clearable="doctorBodyList.length > 1"
+                       v-model="searchData.orgId" @change="searchData.cascader = [];getCascaderB($event)">
+              <el-option v-for="(item) in doctorBodyList" :key="item.value"
+                         :label="item.label" :value="item.value"
+              />
+            </el-select>
+          </div>
+        </div>
+        <div class="screening-item flex-vertical-center-l">
+          <span v-if="cascaderProp.level === 2">医疗机构 / 科室名称:</span>
+          <span v-if="cascaderProp.level === 1">医疗机构:</span>
+          <div class="input">
+            <el-cascader v-if="searchData.orgId" style="width: 100%" size="mini" clearable collapse-tags
+                         :props="cascaderProp" :options="cascaderList" filterable :show-all-levels="false"
+                         v-model="searchData.cascader"
+            />
+            <div v-else class="el-select el-select--mini">
+              <div class="el-input el-input--mini el-input--suffix">
+                <input class="el-input__inner" type="text" readonly="readonly" autocomplete="off" placeholder="请选择">
+              </div>
+            </div>
+          </div>
+        </div>
+
         <div class="screening-item flex-vertical-center-l">
           <span>映射状态:</span>
           <div class="input">
-            <el-select size="mini" placeholder="请选择" v-model="isMap">
+            <el-select size="mini" placeholder="请选择" v-model="searchData.isMapping" clearable>
               <el-option :value="0" label="未映射"></el-option>
               <el-option :value="1" label="已映射"></el-option>
             </el-select>
           </div>
         </div>
         <div class="screening-item flex-vertical-center-l">
-          <span>平台药品名称:</span>
+          <span>机构药品:</span>
           <div class="input">
-            <el-input size="mini" placeholder="请输入药品名称" v-model="yigt"></el-input>
+            <el-input size="mini" placeholder="请输入药品名称" v-model="searchData.matName" @keydown.enter.native="search()"></el-input>
           </div>
         </div>
 
         <el-button type="primary" size="mini" @click="search()">搜索</el-button>
-        <el-button type="warning" size="mini" @click="clearFilter()">清空</el-button>
-        <el-button
-          type="primary"
-          size="mini"
-          @click="$router.push({path:'/index/drugmap?pid='+$route.query.id+'&isMapping='+isMap})"
-        >新增</el-button>
-        <el-button type="warning" size="mini" @click="$router.back()">返回</el-button>
+        <el-button size="mini" @click="clearFilter()">清空</el-button>
+        <el-button size="mini" @click="$router.back()">返回</el-button>
+        <template v-if="editing">
+          <el-button type="warning" size="mini" @click="saveEdit()">保存</el-button>
+          <el-button size="mini" @click="stopEdit()">取消</el-button>
+        </template>
+        <el-button v-else type="success" size="mini" @click="startEdit()">编辑</el-button>
       </div>
     </div>
 
@@ -37,62 +64,56 @@
     <div class="table">
       <div class="today-table">
         <div class="table-container">
-          <el-table :data="tableData" stripe style="width: 100%" border height="100%">
-            <el-table-column prop="id" label="序号" width="100"></el-table-column>
-            <el-table-column label="平台药品">
-              <el-table-column prop="name" label="药品名称"></el-table-column>
-              <el-table-column prop="dw" label="单位"></el-table-column>
-              <!-- <el-table-column prop="drugRemark" label="药品描述">
-              </el-table-column>-->
+          <el-table :data="tableData" v-loading="loading" stripe style="width: 100%" border height="100%"
+                    @select="selectEditRow($event)" @select-all="selectEditRow">
+            <!--<el-table-column prop="id" label="序号" width="100"></el-table-column>-->
+            <el-table-column type="selection" width="55"></el-table-column>
+            <el-table-column label="医共体" prop="orgDrugs.appName" width="200" align="center"></el-table-column>
+            <el-table-column label="医疗机构" prop="orgDrugs.jgmc" align="center"></el-table-column>
+            <el-table-column label="机构药品">
+              <el-table-column label="名称" prop="orgDrugs.name" align="center"></el-table-column>
+              <el-table-column label="规格" prop="orgDrugs.gg" width="200" align="center"></el-table-column>
+              <el-table-column label="单位" prop="orgDrugs.dw" width="200" align="center"></el-table-column>
             </el-table-column>
-            <el-table-column label="第三方药品">
-              <el-table-column prop="name" label="药品名称">
-                <template slot-scope="scope">
-                  <div class="hisName">
-                    <span
-                      v-for="(item,index) in scope.row.orgDrugs"
-                      :key="index"
-                    >{{item.name}}{{index==scope.row.orgDrugs.length-1?'':'/'}}</span>
-                  </div>
-                </template>
-              </el-table-column>
-              <el-table-column prop="type" label="规格">
+            <el-table-column label="映射状态" width="200" align="center">
+              <template slot-scope="scope">
+                <el-tag v-if="scope.row.orgDrugs.isMapping === 1" type="success" size="mini">已映射</el-tag>
+                <el-tag v-else type="danger" size="mini">未映射</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="系统药品">
+              <el-table-column label="名称" align="center">
                 <template slot-scope="scope">
-                  <div>
-                    <span
-                      v-for="(item,index) in scope.row.orgDrugs"
-                      :key="index"
-                    >{{item.gg}}{{index==scope.row.orgDrugs.length-1?'':'/'}}</span>
-                  </div>
+                  <el-select v-if="editing" size="mini" clearable
+                             :loading="editData[scope.$index].loading" :placeholder="editData[scope.$index].placeholder"
+                             filterable remote :remote-method="matSearchRemoteMethod"
+                             v-model="editData[scope.$index].matDrugId" @change="updateEditRow(scope.$index, $event)"
+                             @focus="focusEditRow(scope.$index)"
+                  >
+                    <el-option v-for="option in editData[scope.$index].options" :key="option.value"
+                               :label="option.label" :value="option.value"
+                    />
+                  </el-select>
+                  <div v-else>{{ scope.row.name }}</div>
                 </template>
               </el-table-column>
-              <el-table-column prop="drugRemark" label="单位">
+              <el-table-column label="单位" width="200" align="center">
                 <template slot-scope="scope">
-                  <div>
-                    <span
-                      v-for="(item,index) in scope.row.orgDrugs"
-                      :key="index"
-                    >{{item.dw}}{{index==scope.row.orgDrugs.length-1?'':'/'}}</span>
-                  </div>
+                  <el-input v-if="editing" size="mini" v-model="editData[scope.$index].matDrugDw"></el-input>
+                  <div v-else>{{ scope.row.dw }}</div>
                 </template>
               </el-table-column>
             </el-table-column>
-            <el-table-column label="操作">
-              <template slot-scope="scope">
-                <div class="flex-center operation">
-                  <div
-                    class="flex-center"
-                    @click="$router.push({path:'/index/drugmap?id='+scope.row
-                                    .matDrugId+'&pid='+$route.query.id+'&isMapping='+isMap})"
-                  >编辑</div>
-                  <!-- <div class="flex-center bg-yellow">删除
-                  </div>-->
-                </div>
-              </template>
-            </el-table-column>
           </el-table>
         </div>
         <div class="flex-vertical-center-r today-page">
+          <div>
+            <template v-if="!loading">
+              <span>机构药品:共<el-tag type="info" size="mini" style="margin: 0 4px;">{{ statistics.total }}</el-tag>味,</span>
+              <span>已映射<el-tag type="success" size="mini" style="margin: 0 4px;">{{ statistics.isMapping }}</el-tag>味,</span>
+              <span>未映射<el-tag type="danger" size="mini" style="margin: 0 4px;">{{ statistics.noMapping }}</el-tag>味</span>
+            </template>
+          </div>
           <el-pagination
             background
             layout=" prev, pager, next, jumper, total"
@@ -106,39 +127,107 @@
   </div>
 </template>
 <script>
-import popup from "@/components/Propup.vue";
-import { getDrugMapList, getDrugBM } from "@/api/business.js";
-import { doctorBodySelect } from "@/api/city.js";
-import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
+import popup from '@/components/Propup.vue';
+import {addOrUpdateDrug, getDrugMap, getDrugMapList, searchMyDrug} from '@/api/business.js';
+import {getMedSelect} from '@/api/system';
+import {mapGetters} from 'vuex';
+
+const TAG = -1;
+let cache = new Map();
+let skipWatch = false;
 export default {
   components: {
     popup
   },
   data() {
     return {
-      doctorBody: [], // 医共体选择器数据
-      yigt: "", // 医工体
+      searchData: {
+        pid: '',
+        orgId: '',
+        cascader: [],
+      },
+      doctorBodyList: [], // 医共体选择器数据
+      cascaderList: [],
+      cascaderProp: {
+        level: 1,
+        multiple: true,
+        checkStrictly: true,
+      },
+      loading: false,
       tableData: [],
+      statistics: {},
       page: 1,
       limit: 10,
       total: 0,
-      isMap: 1
+
+      editing: false,
+      editIndex: -1,
+      editData: [],
+      selected: [],
     };
   },
-  created() {
-    this.getDrugMapList();
+  watch: {
+    'searchData.cascader'(value = [], oldValue = []) {
+      if (skipWatch) skipWatch = false;
+      else {
+        skipWatch = true;
+        const vHas = value.some(item => item[0] === TAG);
+        const oHas = oldValue.some(item => item[0] === TAG);
+        if (vHas === oHas) {
+          const _value = value.filter(item => item[0] !== TAG);
+          if (_value.length === this.cascaderList.length - 1) {
+            this.searchData.cascader = [[TAG], ...value];
+          } else {
+            this.searchData.cascader = _value;
+          }
+        } else if (vHas) {
+          this.searchData.cascader = this.cascaderList.map(item => [item.value]);
+        } else {
+          this.searchData.cascader = [];
+        }
+      }
+    },
   },
   beforeRouteEnter(to, from, next) {
-    // 在渲染该组件的对应路由被 confirm 前调用
-    // 不!能!获取组件实例 `this`
-    // 因为当守卫执行前,组件实例还没被创建
-    // 可以通过传一个回调给 next 来访问组件实例
-    next(vm => {
-      // 通过 `vm` 访问组件实例
-      vm.getDrugMapList();
-    });
+    next(vm => vm.load());
   },
+  created() { this.load(); },
+  destroyed() { cache.clear(); },
   methods: {
+    async load() {
+      this.searchData.pid = this.$route.query.pid;
+      const loading = this.$loading({lock: true, text: '加载中...', spinner: 'el-icon-loading'});
+      try {
+        const {groupInfo} = await getDrugMap(this.searchData.pid);
+        for (const {value, children} of groupInfo) cache.set(value, children);
+        this.doctorBodyList = groupInfo;
+        await this.clearFilter();
+      } catch (e) {
+        this.$message.error(e.message);
+        this.$router.back();
+      }
+      loading.close();
+    },
+    async getCascaderB(id) {
+      let list = cache.get(id);
+      if (!Array.isArray(list) || !list.length) {
+        list = await getMedSelect({organizationId: id}).then(res => res && +res.ResultCode === 0
+            ? res.Data.map(item => ({
+              children: [], leaf: false,
+              label: item.name, value: item.pid,
+            }))
+            : [],
+        ).catch(() => []);
+        cache.set(id, list);
+      }
+
+      if (this.cascaderProp.level === 1) {
+        this.cascaderList = list.map(({children, ...item}) => Object.assign(item, {leaf: true}));
+        this.cascaderList.unshift({value: TAG, label: '全选'});
+      } else if (this.cascaderProp.level === 2) {
+        this.cascaderList = list;
+      }
+    },
     sizeC(e) {
       this.page = e;
       this.getDrugMapList();
@@ -148,35 +237,119 @@ export default {
       this.getDrugMapList();
     },
     // 清空搜索条件
-    clearFilter() {
-      this.yigt = "";
-      this.getDrugMapList();
-    },
-    doctorBodyC() {
-      // this.page = 1
-      // this.getDrugList()
+    async clearFilter() {
+      this.searchData = {pid: this.$route.query.pid, orgId: '', cascader: []};
+      if (this.doctorBodyList.length === 1) {
+        this.searchData.orgId = this.doctorBodyList[0].value;
+        await this.getCascaderB(this.searchData.orgId);
+      }
+      this.getDrugMapList().then();
     },
-
     // 获取药品列表
     async getDrugMapList() {
-      let params = {
-        page: this.page,
-        limit: this.limit,
-        isMapping: this.isMap,
-        // orgId: this.getuserinfo.organizationid,
-        matName: this.yigt,
-        orgId: this.$route.query.id
-      };
-      let res = await getDrugMapList(params);
-      if (res.ResultCode == 0) {
-        this.tableData = res.Data.Items;
-        this.total = res.Data.TotalRecordCount;
-        this.tableData.filter((item, index) => {
-          item.id = index + 1;
-          item.type = "his";
-          return item;
-        });
+      this.stopEdit();
+      this.selected = [];
+      this.loading = true;
+      try {
+        const {cascader, ...searchData} = this.searchData;
+        let params = {page: this.page, limit: this.limit, ...searchData};
+        const stitutionsId = cascader.length ? cascader : this.cascaderList.map(item => [item.value]);
+        params.stitutionsIds = stitutionsId.map(item => item[0]).filter(item => item !== TAG).join(',');
+        let res = await getDrugMapList(params);
+        if (res.ResultCode == 0) {
+          this.tableData = res.Data.Items.map(item => Object.assign(item, {id: `${item.orgDrugs.appId}.${item.orgDrugs.jgdm}.${item.orgDrugs.drugId}`}));
+          this.total = res.Data.TotalRecordCount;
+          this.statistics = res.Data.count;
+        } else throw {message: res.ResultInfo};
+      } catch (e) {
+        this.$message.error(e.message);
+      }
+      this.loading = false;
+    },
+    startEdit() {
+      this.editing = true;
+      this.editData = this.tableData.map(item => {
+        return {
+          id: item.id,
+          orgId: item.orgDrugs.appId,
+          jgDrugId: item.orgDrugs.drugId,
+          jgDrugName: item.orgDrugs.name,
+          matDrugId: item.matDrugId,
+          matDrugName: item.name,
+          matDrugDw: item.dw,
+
+          loading: false,
+          placeholder: item.name || '请输入药品名称搜索',
+          options: item.matDrugId ? [{value: item.matDrugId, label: item.name, dw: item.dw}] : [],
+        };
+      });
+    },
+    stopEdit() {
+      this.editing = false;
+      this.editData = [];
+    },
+    async saveEdit() {
+      try {
+        const data = [];
+        for (const {orgId, jgDrugId, matDrugId, matDrugName, matDrugDw} of this.editData) {
+          if (matDrugId && !matDrugDw) throw `请补充完整系统药品单位`;
+          data.push({orgId, jgDrugId, matDrugId, matDrugName, matDrugDw, isMapping: matDrugId ? 1 : 0});
+        }
+        await addOrUpdateDrug(data).then(res => { if (res.ResultCode !== 0) throw {message: res.ResultInfo};});
+        this.getDrugMapList().then();
+      } catch (e) {
+        if (typeof e === 'string') this.$message.warning(e);
+        else this.$message.error(e.message);
+      }
+    },
+    selectEditRow(selection, row) {
+      this.selected = selection.map(item => item.id);
+      if (this.editing && selection.length > 1) this.syncEditRow(row);
+    },
+    focusEditRow(index, name = '') {
+      this.editIndex = index;
+      const item = this.editData[index];
+      if (!item.loading && !item.options.length) {
+        if (!name) {try {name = (item.matDrugName || item.jgDrugName).match(/[\u4E00-\u9FFF]+/g)[0];} catch (e) {name = '';}}
+        this.matSearchRemoteMethod(name, index).then(options => { if (!options.length) this.matSearchRemoteMethod(' ', index); });
+      }
+    },
+    syncEditRow(row) {
+      const selection = this.selected.map(id => this.editData.find(item => item.id === id));
+      if (row == null) row = selection.find(item => item.matDrugId);
+      if (row == null) return;
+      for (const item of selection) {
+        item.matDrugId = row.matDrugId;
+        item.matDrugName = row.matDrugName;
+        item.matDrugDw = row.matDrugDw;
+        item.options = row.options.map(item => Object.assign({}, item));
       }
+    },
+    updateEditRow(index, value) {
+      const item = this.editData[index];
+      if (value) {
+        const option = item.options.find(item => item.value === value);
+        item.matDrugName = option.label;
+        item.matDrugDw = option.dw;
+      } else {
+        item.matDrugName = '';
+        item.matDrugDw = '';
+      }
+      if (this.selected.includes(item.id)) this.syncEditRow(item);
+    },
+    async matSearchRemoteMethod(name, index) {
+      if (index == null) index = this.editIndex;
+      const item = this.editData[index];
+      if (!name) item.options = item.matDrugId ? [{value: item.matDrugId, label: item.name}] : [];
+      else {
+        item.loading = true;
+        item.options = await searchMyDrug({name: name.trim()}).then(res => {
+          if (res.ResultCode === 0)
+            return res.Data.map(item => ({value: item.drugId, label: item.name, dw: item.dw}));
+        }).catch(() => []);
+        item.loading = false;
+      }
+      return item.options;
     }
   },
 
@@ -196,6 +369,10 @@ export default {
   margin-top: 10px;
   height: 72vh;
 
+  .flex-vertical-center-r {
+    justify-content: space-between;
+  }
+
   .table-btns {
     margin-bottom: 13px;