feat: 库存管理
出库管理、采购管理、报废管理 新增时物品查询增加机构限制
18个文件已修改
1435 ■■■■■ 已修改文件
admin-web/src/main.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/store/modules/user.js 398 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/styles/store.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/utils/base.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/dashboard/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/departmentitem/itemdis/distribution/detail.vue 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/departmentitem/itemdis/distribution/edit.vue 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/departmentitem/itemdis/distribution/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/accessStock/outbound/detail.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/accessStock/outbound/edit.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/ledger/inventoryAlert/edit.vue 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/procure/purchaseOrder/edit.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/scrap/itemScrapping/detail.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/scrap/itemScrapping/edit.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/transfer/transferApplication/detail.vue 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/transfer/transferApplication/edit.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/stock/transfer/transferissue/detail.vue 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/main.js
@@ -33,7 +33,7 @@
import './directive/num';
import searchInfo from '@/utils/changeColor';
import exportExcel from '@/utils/exportExcel'; // 导出
import {downLoad, previewDoc} from '@/utils/base';
import {downLoad} from '@/utils/base';
import '@/mock'
Vue.config.devtools = true;
@@ -44,7 +44,6 @@
});
Vue.prototype.exportExcelUtils = exportExcel;
Vue.prototype.previewDoc = previewDoc;
Vue.prototype.downLoad = downLoad;
Vue.prototype.$searchInfo = searchInfo;
Vue.use(VueLazyload, {
admin-web/src/store/modules/user.js
@@ -17,223 +17,225 @@
import {encrypt} from '@/utils/jsencrypt'
const state = {
  info: getUser(),
  userInfo:getUserDetail(),//用户详细信息
  token: getToken(),
  userId:'',
  name: '',
  avatar: '',
  introduction: '',
  roles: [],
  isLogin: Cookies.get('isLogin'),
  permissions: [],
  myButtonPermission:[],
  captcha: {
    captchaVerification: '',
    secretKey: '',
    token: '',
  }, //滑块验证token
    info: getUser(),
    userInfo: getUserDetail(),//用户详细信息
    token: getToken(),
    userId: '',
    name: '',
    avatar: '',
    introduction: '',
    roles: [],
    isLogin: Cookies.get('isLogin'),
    permissions: [],
    myButtonPermission: [],
    captcha: {
        captchaVerification: '',
        secretKey: '',
        token: '',
    }, //滑块验证token
};
const mutations = {
  SET_USER_INFO:(state, info)=>{
    // 设置详细信息
    state.userInfo = info
    setUserDetail(info)
  },
  SET_MYBUTTONPERMISSION: (state, myButtonPermission) => {
    state.myButtonPermission = myButtonPermission
  },
  SET_TOKEN: (state, token) => {
    state.token = token;
    setToken(token)
  },
  SET_USERINFO: (state, user) =>{
    // 保存用户信息
    state.info = user
    setUserInfo(user)
  },
  SET_ISLOGIN: (state, isLogin) => {
    state.isLogin = isLogin;
    Cookies.set(isLogin);
  },
  SET_INTRODUCTION: (state, introduction) => {
    state.introduction = introduction;
  },
  SET_USERID: (state, id) => {
    state.userId = id;
  },
  SET_NAME: (state, name) => {
    state.name = name;
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar;
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles;
  },
  SET_PERMISSIONS: (state, permissions) => {
    state.permissions = permissions;
  },
  SET_CAPTCHA: (state, captcha) => {
    state.captcha = captcha;
  }
    SET_USER_INFO: (state, info) => {
        // 设置详细信息
        state.userInfo = info
        setUserDetail(info)
    },
    SET_MYBUTTONPERMISSION: (state, myButtonPermission) => {
        state.myButtonPermission = myButtonPermission
    },
    SET_TOKEN: (state, token) => {
        state.token = token;
        setToken(token)
    },
    SET_USERINFO: (state, user) => {
        // 保存用户信息
        state.info = user
        setUserInfo(user)
    },
    SET_ISLOGIN: (state, isLogin) => {
        state.isLogin = isLogin;
        Cookies.set(isLogin);
    },
    SET_INTRODUCTION: (state, introduction) => {
        state.introduction = introduction;
    },
    SET_USERID: (state, id) => {
        state.userId = id;
    },
    SET_NAME: (state, name) => {
        state.name = name;
    },
    SET_AVATAR: (state, avatar) => {
        state.avatar = avatar;
    },
    SET_ROLES: (state, roles) => {
        state.roles = roles;
    },
    SET_PERMISSIONS: (state, permissions) => {
        state.permissions = permissions;
    },
    SET_CAPTCHA: (state, captcha) => {
        state.captcha = captcha;
    }
};
const actions = {
  // user login
  login({ commit }, userInfo) {
    // const { account, pwd, key, code, wxCode } = userInfo;
    const username = userInfo.account.trim()
    // const password = userInfo.password
    const password = encrypt(userInfo.password)
    const code = userInfo.code
    const uuid = userInfo.uuid
    const loginType = userInfo.loginType
    const verifyType = userInfo.verifyType
    Loading.service();
    return new Promise((resolve, reject) => {
      // login(userInfo)
      login(username, password, code, uuid, loginType, verifyType)
        .then((data) => {
          let loadingInstance = Loading.service();
          loadingInstance.close();
          commit('SET_TOKEN', data.token);
          commit('SET_USERINFO', data.userInfo);
          getCurInfo().then(res=>{
            commit('SET_USER_INFO',res)
          })
          resolve();
        })
        .catch((error) => {
          reject(error);
    // user login
    login({commit}, userInfo) {
        // const { account, pwd, key, code, wxCode } = userInfo;
        const username = userInfo.account.trim()
        // const password = userInfo.password
        const password = encrypt(userInfo.password)
        const code = userInfo.code
        const uuid = userInfo.uuid
        const loginType = userInfo.loginType
        const verifyType = userInfo.verifyType
        Loading.service();
        return new Promise((resolve, reject) => {
            // login(userInfo)
            login(username, password, code, uuid, loginType, verifyType)
                .then((data) => {
                    let loadingInstance = Loading.service();
                    loadingInstance.close();
                    commit('SET_TOKEN', data.token);
                    commit('SET_USERINFO', data.userInfo);
                    getCurInfo().then(res => {
                        commit('SET_USER_INFO', res)
                        resolve();
                    }).catch((error) => {
                        reject(error);
                    })
                })
                .catch((error) => {
                    reject(error);
                });
        });
    });
  },
    },
  // 短信是否登录
  isLogin({ commit }, userInfo) {
    return new Promise((resolve, reject) => {
      isLoginApi()
        .then(async (res) => {
          commit('SET_ISLOGIN', res.isLogin);
          resolve(res);
        })
        .catch((res) => {
          commit('SET_ISLOGIN', false);
          reject(res);
    // 短信是否登录
    isLogin({commit}, userInfo) {
        return new Promise((resolve, reject) => {
            isLoginApi()
                .then(async (res) => {
                    commit('SET_ISLOGIN', res.isLogin);
                    resolve(res);
                })
                .catch((res) => {
                    commit('SET_ISLOGIN', false);
                    reject(res);
                });
        });
    });
  },
    },
  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token)
        .then(data => {
          if (!data) {
            reject('Verification failed, please Login again.');
          }
          let roles = data.roleIds;
          if (!roles || roles.length <= 0) {
            reject('getInfo: roles must be a non-null array!');
          }
          // 获取并设置修改密码的状态
          setCPass(data.force_change_pass)
          if( data.force_change_pass ){
            router.push({ path: '/resetPwd' });
            reject(data);
          }
          commit('SET_USERID', data.id);
          commit('SET_ROLES', roles);
          commit('SET_NAME', data.account);
          commit('SET_AVATAR', 'http://kaifa.crmeb.net/system/images/admin_logo.png');
          commit('SET_INTRODUCTION', data.realName);
          commit('SET_PERMISSIONS', data.permissions); //权限标识
          resolve(data);
        })
        .catch((error) => {
          reject(error);
    // get user info
    getInfo({commit, state}) {
        return new Promise((resolve, reject) => {
            getInfo(state.token)
                .then(data => {
                    if (!data) {
                        reject('Verification failed, please Login again.');
                    }
                    let roles = data.roleIds;
                    if (!roles || roles.length <= 0) {
                        reject('getInfo: roles must be a non-null array!');
                    }
                    // 获取并设置修改密码的状态
                    setCPass(data.force_change_pass)
                    if (data.force_change_pass) {
                        router.push({path: '/resetPwd'});
                        reject(data);
                    }
                    commit('SET_USERID', data.id);
                    commit('SET_ROLES', roles);
                    commit('SET_NAME', data.account);
                    commit('SET_AVATAR', 'http://kaifa.crmeb.net/system/images/admin_logo.png');
                    commit('SET_INTRODUCTION', data.realName);
                    commit('SET_PERMISSIONS', data.permissions); //权限标识
                    resolve(data);
                })
                .catch((error) => {
                    reject(error);
                });
        });
    });
  },
    },
  // user logout
  logout({ commit, state, dispatch }) {
    Loading.service();
    return new Promise((resolve, reject) => {
      logout(state.token)
        .then(() => {
          let loadingInstance = Loading.service();
          loadingInstance.close();
          commit('SET_TOKEN', '');
          commit('SET_ROLES', []);
          commit('SET_PERMISSIONS', []);
          commit('SET_CONNECTION_CLEAR', null) // 断开链接
          removeToken();
          resetRouter();
          // localStorage.clear();
          Cookies.remove('storeStaffList');
          Cookies.remove('JavaInfo');
          localStorage.clear('sidebarRouters');
          sessionStorage.removeItem('token');
          // reset visited views and cached views
          // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
          dispatch('tagsView/delAllViews', null, { root: true });
    // user logout
    logout({commit, state, dispatch}) {
        Loading.service();
        return new Promise((resolve, reject) => {
            logout(state.token)
                .then(() => {
                    let loadingInstance = Loading.service();
                    loadingInstance.close();
                    commit('SET_TOKEN', '');
                    commit('SET_ROLES', []);
                    commit('SET_PERMISSIONS', []);
                    commit('SET_CONNECTION_CLEAR', null) // 断开链接
                    removeToken();
                    resetRouter();
                    // localStorage.clear();
                    Cookies.remove('storeStaffList');
                    Cookies.remove('JavaInfo');
                    localStorage.clear('sidebarRouters');
                    sessionStorage.removeItem('token');
                    // reset visited views and cached views
                    // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
                    dispatch('tagsView/delAllViews', null, {root: true});
          resolve();
        })
        .catch((error) => {
          reject(error);
                    resolve();
                })
                .catch((error) => {
                    reject(error);
                });
        });
    });
  },
    },
  // remove token
  resetToken({ commit }) {
    return new Promise((resolve) => {
      commit('SET_TOKEN', '');
      commit('SET_ROLES', []);
      removeToken();
      resolve();
    });
  },
  // // 设置token
  // setToken({ commit }, state) {
  //   return new Promise((resolve) => {
  //     commit('SET_TOKEN', state.token);
  //     // Cookies.set('JavaInfo', JSON.stringify(state));
  //     setToken(data.token);
  //     resolve();
  //   });
  // },
    // remove token
    resetToken({commit}) {
        return new Promise((resolve) => {
            commit('SET_TOKEN', '');
            commit('SET_ROLES', []);
            removeToken();
            resolve();
        });
    },
    // // 设置token
    // setToken({ commit }, state) {
    //   return new Promise((resolve) => {
    //     commit('SET_TOKEN', state.token);
    //     // Cookies.set('JavaInfo', JSON.stringify(state));
    //     setToken(data.token);
    //     resolve();
    //   });
    // },
  // dynamically modify permissions
  changeRoles({ commit, dispatch }, role) {
    return new Promise(async (resolve) => {
      // const token = role + '-token';
      //
      // commit('SET_TOKEN', token);
      // // setToken(token);
      //
      const { roles } = await dispatch('getInfo');
      // resetRouter();
      // generate accessible routes map based on roles
      const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true });
      // dynamically add accessible routes
      router.addRoutes(accessRoutes);
    // dynamically modify permissions
    changeRoles({commit, dispatch}, role) {
        return new Promise(async (resolve) => {
            // const token = role + '-token';
            //
            // commit('SET_TOKEN', token);
            // // setToken(token);
            //
            const {roles} = await dispatch('getInfo');
            // resetRouter();
            // generate accessible routes map based on roles
            const accessRoutes = await dispatch('permission/generateRoutes', roles, {root: true});
            // dynamically add accessible routes
            router.addRoutes(accessRoutes);
      // reset visited views and cached views
      dispatch('tagsView/delAllViews', null, { root: true });
            // reset visited views and cached views
            dispatch('tagsView/delAllViews', null, {root: true});
      resolve();
    });
  },
            resolve();
        });
    },
};
export default {
  namespaced: true,
  state,
  mutations,
  actions,
    namespaced: true,
    state,
    mutations,
    actions,
};
admin-web/src/styles/store.scss
@@ -187,6 +187,8 @@
    cursor: pointer;
    .img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
admin-web/src/utils/base.js
@@ -23,24 +23,6 @@
  return getBaseUrl() + '/pc/fin/file/uploadMore'
}
// 预览文件
export function previewDoc(obj) {
  const routeUrl = this.$router.resolve({url: '/webOffice/preview'})
  let url = obj.url
  const wordLIst = ['doc', 'docm', 'docx', 'docxf', 'dot', 'dotm', 'dotx', 'epub', 'fodt', 'fb2', 'htm', 'html', 'mht', 'odt', 'oform', 'ott', 'oxps', 'pdf', 'rtf', 'txt', 'djvu', 'xml', 'xps', 'csv', 'fods', 'ods', 'ots', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', 'fodp', 'odp', 'otp', 'pot', 'potm', 'potx', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx']
  const arr = url.split('.')
  if (arr.length === 2) {
    if (wordLIst.includes(arr[arr.length - 1])) {
      if (obj.url.indexOf('http') !== 0) {
        url = SettingIplatform.ftpUrl + url
      }
      const newWindow = window.open(routeUrl.href + '?url=' + encodeURIComponent(url) + '&docName=' + encodeURIComponent(encodeURIComponent(obj.fileName)), '_blank')
      if (newWindow) newWindow.opener = null
    } else {
      Message.warning('文件格式不支持预览!')
    }
  }
}
// 下载文件
export function downLoad(obj) {
admin-web/src/views/dashboard/index.vue
@@ -182,6 +182,7 @@
    };
  },
  created() {
    console.log(this.userInfo)
    this.getAuditList()
    this.getWarningList()
    getGoodsNumPrice().then(res => {
admin-web/src/views/departmentitem/itemdis/distribution/detail.vue
@@ -1,44 +1,44 @@
<template>
  <win-lg class="stock-detail" :title="setting.title" @close="close" :width="'800px'" :loading="loading">
  <win-md class="stock-detail" :title="setting.title" @close="close" :width="'800px'" :loading="loading">
    <el-row :gutter="20">
      <el-col :span="8">
        <span>分发单号:</span>
        <span>{{detail.businessFormCode}}</span>
        <span>{{ detail.businessFormCode }}</span>
      </el-col>
      <el-col :span="8">
        <span>所属机构:</span>
        <span>{{detail.outAgencyName}}</span>
        <span>{{ detail.outAgencyName }}</span>
      </el-col>
      <el-col :span="8">
        <span>部门:</span>
        <span>{{detail.inWarehouseName}}</span>
        <span>{{ detail.inWarehouseName }}</span>
      </el-col>
    </el-row>
    <el-row :gutter="20" style="margin-top: 20px">
      <el-col :span="8">
        <span>分发人:</span>
        <span>{{detail.outOperatorName}}</span>
        <span>{{ detail.outOperatorName }}</span>
      </el-col>
      <el-col :span="8">
        <span>分发时间:</span>
        <span>{{detail.createTime|formatTime}}</span>
        <span>{{ detail.createTime|formatTime }}</span>
      </el-col>
      <el-col :span="8">
        <span>领取人:</span>
        <span>{{detail.inOperatorName}}</span>
        <span>{{ detail.inOperatorName }}</span>
      </el-col>
    </el-row>
    <el-row :gutter="20" style="margin-top: 20px">
      <el-col :span="8">
        <span>联系电话:</span>
        <span>{{detail.tel}}</span>
        <span>{{ detail.tel }}</span>
      </el-col>
    </el-row>
    <el-row :gutter="20" style="margin-top: 20px">
      <el-col class="img-row" :span="24">
        <span>分发手续:</span>
        <div class="img-box" v-for="(item, index) in fileList" :key="index" @click="handlePreview(item)">
          <img class="img" :src="getUrl(item.path)" alt="" />
          <img class="img" :src="getUrl(item.path)" alt=""/>
        </div>
      </el-col>
    </el-row>
@@ -60,13 +60,13 @@
      <el-form>
        <el-row class="card3" v-for="(mItem, mIndex) in goodsItem.models" :key="mIndex">
          <el-col style="padding: 10px 0" :span="10">
            <el-form-item label="规格型号1:"  style="margin-bottom: 0">
            <el-form-item label="规格型号1:" style="margin-bottom: 0">
              {{ mItem.baseGoodsModelsName }}
            </el-form-item>
          </el-col>
          <el-col style="padding: 10px 0" :span="4">
            <el-form-item label="单位:" style="margin-bottom: 0" label-width="80">
              {{mItem.unit}}
              {{ mItem.unit }}
            </el-form-item>
          </el-col>
          <!--A类商品 -->
@@ -74,17 +74,17 @@
            <el-row v-for="(user, u) in mItem.useInfo" :key="u">
              <el-col :span="6">
                <el-form-item label="分发数量:" style="margin-bottom: 0" label-width="80">
                  {{user.num}}
                  {{ user.num }}
                </el-form-item>
              </el-col>
              <el-col :span="6">
                <el-form-item label="使用人:" style="margin-bottom: 0" label-width="80">
                  {{user.name}}
                  {{ user.name }}
                </el-form-item>
              </el-col>
              <el-col :span="6">
                <el-form-item label="联系电话:" style="margin-bottom: 0" label-width="80">
                  {{user.tel}}
                  {{ user.tel }}
                </el-form-item>
              </el-col>
            </el-row>
@@ -92,7 +92,7 @@
          <!--BC类商品 -->
          <el-col style="padding: 10px 0" :span="10" v-else>
            <el-form-item label="分发数量:" prop="counts" style="margin-bottom: 0" label-width="80">
              {{mItem.counts}}
              {{ mItem.counts }}
            </el-form-item>
          </el-col>
        </el-row>
@@ -102,19 +102,19 @@
    <div id="uploadPreviewImages" style="display: none">
      <span v-for="(src, index) in fileList" :key="index">
        <img
          v-if="checkImg(src.name)"
          class="v-img"
          :src="src.url"
          :alt="src.name"
          style="width: 100px; height: 100px"
            v-if="checkImg(src.name)"
            class="v-img"
            :src="src.url"
            :alt="src.name"
            style="width: 100px; height: 100px"
        />
      </span>
    </div>
  </win-lg>
  </win-md>
</template>
<script>
import {transferDetail} from '@/api/stock/transfer';
import winLg from '@/components/win/win-lg';
import winMd from '@/components/win/win-md';
import * as DateFormatter from '@/utils/DateFormatter';
import {getDownUrl} from '@/utils/base';
import Viewer from 'viewerjs';
@@ -129,16 +129,17 @@
      return de
    }
  },
  components: { winLg },
  components: {winMd},
  props: {
    setting: {
      type: Object,
      default: () => {},
      default: () => {
      },
    },
  },
  data() {
    return {
      loading:false,
      loading: true,
      fileList: [],
      detail: {
        baseCategoryName: '',
@@ -165,8 +166,9 @@
    }
  },
  created() {
    transferDetail({ id: this.setting.id }).then((res) => {
    transferDetail({id: this.setting.id}).then((res) => {
      this.detail = res;
      this.loading = false
      this.fileList = this.detail.procureDoc ? JSON.parse(this.detail.procureDoc) : [];
      this.$nextTick(() => {
        this.initPreviewImg();
@@ -226,18 +228,11 @@
</script>
<style lang="scss" scoped>
@import url(../../../../styles/store.scss);
.card3 {
  padding: 10px;
  margin-top: 10px;
  border-radius: 2px;
  background: #ffffff;
}
.img-box{
  width: 100px;
  height: 100px;
}
.img-box .img{
  width: 100px;
  height: 100px;
}
</style>
admin-web/src/views/departmentitem/itemdis/distribution/edit.vue
@@ -1,11 +1,11 @@
<template>
  <win-md class="stock-edit" title="物品分发" @close="close" width="900px">
    <el-form v-loading="loading" class="form" ref="ruleForm" :model="formData" :rules="rules" label-width="120px">
  <win-md class="stock-edit" title="物品分发" @close="close" width="900px" :loading="loading">
    <el-form class="form" ref="ruleForm" :model="formData" :rules="rules" label-width="120px">
      <div class="main-w">
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="12">
            <el-form-item label="部门" prop="departmentId">
              <el-select ref="department" v-model="formData.departmentId" placeholder="请选择" style="width: 100%">
              <el-select ref="department" v-model="formData.departmentId" clearable placeholder="请选择" style="width: 100%">
                <el-option v-for="item in departmentOptions" :key="item.id" :label="item.name" :value="item.id"
                           clearable/>
              </el-select>
@@ -75,7 +75,7 @@
              >
                <el-select
                    v-model="goodsItem.baseGoodsTemplateId"
                    placeholder="请选择物品"
                    :placeholder="goodsItem.baseCategoryId?'请选择物品':'请先选择物品分类'"
                    filterable
                    clearable
                    :disabled="!goodsItem.baseCategoryId"
@@ -97,11 +97,11 @@
              <el-form-item
                  label="规格型号"
                  :prop="`transferGoods.${goodsIndex}.modelsIds`"
                  :rules="{required: true,message: '请选择',trigger: 'change',}">
                  :rules="{required: true,message: '请选择规格型号',trigger: 'change',}">
                <el-select
                    v-model="goodsItem.modelsIds"
                    multiple
                    placeholder="请选择物品规格型号"
                    :placeholder="goodsItem.baseCategoryId?'请选择规格型号':'请先选择物品名称'"
                    :disabled="!goodsItem.baseCategoryId"
                    @change="modelChange($event, goodsIndex)"
                    @remove-tag="modelRemoveTag($event, goodsIndex)"
@@ -290,17 +290,7 @@
        }
        callback();
      },
      loading: false,
      buyTypeOptions: [
        {
          label: '集采',
          value: '1',
        },
        {
          label: '自采',
          value: '2',
        },
      ],
      loading: true,
      fileList: [],
      departmentOptions: [], // 部门列表
      categoryOptions: [], // 物品分类列表
@@ -342,7 +332,7 @@
        createTime: [{required: true, message: '请选择分发时间', trigger: 'change'}],
        procureDoc: [{required: true, message: '请上传审批手续', trigger: 'change'}],
        baseCategoryIds: [{required: true, message: '请选择', trigger: 'change'}],
        baseGoodsTemplateId: [{validator: checkGoodsTemplateId, trigger: ['blur', 'change']}],
        baseGoodsTemplateId: [{required: true, message: '请选择', trigger: 'change'},{validator: checkGoodsTemplateId, trigger: ['blur', 'change']}],
        modelsIds: [{required: true, message: '请选择', trigger: 'change'}],
        transferGoods: [
          {
@@ -369,7 +359,6 @@
        title: '上传',
        max: 20, // 最大大小,单位M
        num: 10, // 支持上传图片个数
        accept: '.jpg,.png', // 限制格式
        tip: '', // 提示 默认:`只能上传${this.defaultSettings.num}个${this.defaultSettings.accept}文件,且不超过${this.defaultSettings.max}kb`
        uploadUrl: getUploadUrl(), // 上传路径
        multiple: true, // 是否支持批量上传
@@ -386,34 +375,13 @@
  },
  methods: {
    async init() {
      this.getdeptmentList();
      this.getgoodsTemplate();
      this.getgoodsModel();
      await this.getdeptmentList();
      // 获取物品分类列表
      const treeRes = await getTree();
      this.categoryOptions = this.removeEmptyChildren(treeRes);
      /*if (this.setting.id) {
        const detail = await procureDetail({ id: this.setting.id });
        this.formData = Object.assign(this.formData, detail);
        if (this.formData.procureDoc) {
          this.fileList = JSON.parse(this.formData.procureDoc);
        }
        this.$set(this.formData, 'buyType', this.formData.buyType.toString());
        this.formData.procureTime = this.formData.procureTime.toString();
        this.formData.transferGoods.map((item, index) => {
          this.$set(
            this.formData.transferGoods[index],
            'modelsIds',
            item.models.map((v) => v.baseGoodsModelsId),
          );
          this.getgoodsTemplate(item.baseCategoryId, index);
          this.getgoodsModel(item.baseGoodsTemplateId, index);
          item.models.forEach((child, childIndex) => {
            this.$set(this.formData.transferGoods[index].models[childIndex], 'priceYuan', child.price / 100);
          });
          return item;
        });
      }*/
      this.loading = false
      this.getgoodsTemplate();
      this.getgoodsModel();
    },
    // 获取入库仓库列表
@@ -568,7 +536,6 @@
    // 提交
    handleSubmit() {
      console.log('formData', this.formData);
      if(this.loading) return
      this.$refs['ruleForm'].validate((valid) => {
        if (valid) {
          this.formData.transferGoods.map(goods => {
@@ -585,6 +552,7 @@
          this.formData.departmentName = this.$refs.department.selected.currentLabel
          this.formData.outAgencyId = this.userInfo.tenantId
          if(this.loading) return
          this.loading = true
          transferAdd(this.formData)
              .then((res) => {
admin-web/src/views/departmentitem/itemdis/distribution/index.vue
@@ -172,6 +172,7 @@
        qryType: 1,
        pageNum: this.pageNum,
        pageSize: this.pageSize,
        outAgencyId: this.userInfo.tenantId,
        ...this.filterFrom,
      }).then((res) => {
        this.list = res.datas;
admin-web/src/views/stock/accessStock/outbound/detail.vue
@@ -24,10 +24,12 @@
        <span>{{ detail.dealTime | formatTime }}</span>
      </el-col>
    </el-row>
    <el-row v-if="detail.procureDoc" :gutter="20" style="margin-top: 20px">
    <el-row v-if="fileList && fileList.length" :gutter="20" style="margin-top: 20px">
      <el-col class="img-row" :span="24">
        <span>出库手续:</span>
        <div class="img-box"></div>
        <div class="img-box" v-for="(item, index) in fileList" :key="index" @click="handlePreview(item)">
          <img class="img" :src="getUrl(item.path)" alt=""/>
        </div>
      </el-col>
    </el-row>
    <div class="goods-card" v-for="(goodsItem, goodsIndex) in detail.fromOutputGoods" :key="goodsIndex">
@@ -64,6 +66,17 @@
        </el-table-column>
      </el-table>
    </div>
    <div id="uploadPreviewImages" style="display: none">
      <span v-for="(src, index) in fileList" :key="index">
        <img
            v-if="checkImg(src.name)"
            class="v-img"
            :src="src.url"
            :alt="src.name"
            style="width: 100px; height: 100px"
        />
      </span>
    </div>
  </win-md>
</template>
<script>
@@ -71,6 +84,11 @@
import winMd from '@/components/win/win-md';
import * as DateFormatter from '@/utils/DateFormatter';
import {getDownUrl} from "@/utils/base";
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
let viewer = null;
export default {
  components: { winMd },
@@ -83,6 +101,7 @@
  data() {
    return {
      loading:true,
      fileList: [],
      detail: {
        categoryName: '',
        businessFormCode: '',
@@ -107,10 +126,50 @@
  created() {
    outputDetail({ id: this.setting.id }).then((res) => {
      this.detail = res;
      this.loading = false
      this.fileList = this.detail.doc ? JSON.parse(this.detail.doc) : [];
      this.loading = false;
      this.$nextTick(() => {
        this.initPreviewImg();
      });
    });
  },
  methods: {
    initPreviewImg() {
      if (viewer != null) {
        viewer.destroy();
      }
      const ViewerDom = document.querySelector('#uploadPreviewImages');
      viewer = new Viewer(ViewerDom, {});
    },
    handlePreview(file) {
      if (!this.checkImg(file.name)) {
        return false;
      }
      let index = 0;
      for (let i = 0; i < this.fileList.length; i++) {
        const f = this.fileList[i];
        if (this.checkImg(f.name)) {
          if (file.id == f.id) {
            break;
          }
          index++;
        }
      }
      viewer.view(index);
    },
    checkImg(name) {
      const suffix = name.substring(name.lastIndexOf('.'), name.length);
      const imgArray = ['.jpg', '.jpeg', '.png', '.bmp'];
      return imgArray.indexOf(suffix) >= 0;
    },
    getUrl(path) {
      if (path.substr(0, 7).toLowerCase() == 'http://' || path.substr(0, 8).toLowerCase() == 'https://') {
        return path;
      } else {
        return getDownUrl() + path;
      }
    },
    close() {
      this.$emit('close')
    },
admin-web/src/views/stock/accessStock/outbound/edit.vue
@@ -5,7 +5,7 @@
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="12">
            <el-form-item label="出库仓库" prop="warehouseId">
              <el-select v-model="formData.warehouseId" placeholder="请选择" style="width: 100%">
              <el-select v-model="formData.warehouseId" placeholder="请选择" clearable style="width: 100%">
                <el-option
                  v-for="item in warehouseOptions"
                  :key="item.id"
@@ -20,6 +20,7 @@
              <el-date-picker
                v-model="formData.dealTime"
                type="datetime"
                clearable
                value="yyyy-MM-dd HH:mm:ss"
                value-format="yyyyMMddHHmmss"
                placeholder="请选择日期"
@@ -31,7 +32,7 @@
        </el-row>
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="24">
            <el-form-item label="出库手续" prop="procureDoc">
            <el-form-item label="出库手续" prop="doc">
              <upload ref="uploadRef" :settings="uploadSettings" @on-change="uploadChange"></upload>
            </el-form-item>
          </el-col>
@@ -41,7 +42,7 @@
            <el-col :span="12">
              <el-form-item
                label="物品分类"
                :prop="`goods[${goodsIndex}].baseCategoryIds`"
                :prop="`goods.${goodsIndex}.baseCategoryId`"
                :rules="{
                  required: true,
                  message: '请选择',
@@ -49,9 +50,13 @@
                }"
              >
                <el-cascader
                  v-model="goodsItem.baseCategoryIds"
                  v-model="goodsItem.baseCategoryId"
                  :options="categoryOptions"
                  :props="{ value: 'id' }"
                  placeholder="请选择物品分类"
                  :props="{ value: 'id',emitPath: false }"
                  :show-all-levels="false"
                  filterable
                  clearable
                  @change="categoryChange($event, goodsIndex)"
                  style="width: 100%"
                ></el-cascader>
@@ -60,13 +65,14 @@
            <el-col :span="12">
              <el-form-item
                label="物品名称"
                :prop="`goods[${goodsIndex}].baseGoodsTemplateId`"
                :prop="`goods.${goodsIndex}.baseGoodsTemplateId`"
                :rules="rules.baseGoodsTemplateId"
              >
                <el-select
                  v-model="goodsItem.baseGoodsTemplateId"
                  placeholder="请先择物品分类"
                  :placeholder="goodsItem.baseCategoryId?'请选择物品':'请先选择物品分类'"
                  filterable
                  clearable
                  :disabled="!goodsItem.baseCategoryId"
                  style="width: 100%"
                  @change="goodsTemplateChange($event, goodsIndex)"
@@ -85,20 +91,22 @@
            <el-col :span="12">
              <el-form-item
                label="规格型号"
                :prop="`goods[${goodsIndex}].modelsIds`"
                :prop="`goods.${goodsIndex}.modelsIds`"
                :rules="{
                  required: true,
                  message: '请选择',
                  message: '请选择规格型号',
                  trigger: 'change',
                }"
              >
                <el-select
                  v-model="goodsItem.modelsIds"
                  multiple
                  placeholder="请先择物品名称"
                  clearable
                  :placeholder="goodsItem.baseCategoryId?'请选择规格型号':'请先选择物品名称'"
                  :disabled="!goodsItem.baseCategoryId"
                  @change="modelChange($event, goodsIndex)"
                  @remove-tag="modelRemoveTag($event, goodsIndex)"
                  @clear="modelRemoveTag(-1, goodsIndex)"
                  style="width: 100%"
                >
                  <el-option
@@ -131,8 +139,7 @@
              <template slot-scope="scope">
                <el-form-item
                  label-width="0"
                  style="margin-bottom: 0"
                  :prop="`goods[${goodsIndex}].models[${scope.$index}].counts`"
                  :prop="`goods.${goodsIndex}.models.${scope.$index}.counts`"
                  :rules="rules.counts"
                >
                  <el-input
@@ -213,7 +220,7 @@
      formData: {
        warehouseId: '', // 出库仓库id
        dealTime: '', // 出库时间
        procureDoc: '',
        doc: '',
        goods: [],
      },
      goodsItem: {
@@ -233,13 +240,12 @@
        unit: null, //单位
      },
      rules: {
        warehouseId: [{ required: true, message: '请选择', trigger: 'change' }],
        dealTime: [{ required: true, message: '请选择', trigger: 'change' }],
        buyType: [{ required: true, message: '请选择', trigger: 'change' }],
        baseCategoryIds: [{ required: true, message: '请选择', trigger: 'change' }],
        baseGoodsTemplateId: [{ validator: checkGoodsTemplateId, trigger: ['blur', 'change'] }],
        modelsIds: [{ required: true, message: '请选择', trigger: 'change' }],
        warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
        dealTime: [{ required: true, message: '请选择时间', trigger: 'change' }],
        doc: [{required: true, message: '请上传审批手续', trigger: 'change'}],
        baseCategoryId: [{ required: true, message: '请选择物品分类', trigger: 'change' }],
        baseGoodsTemplateId: [{ required: true, message: '请选择物品', trigger: 'change' },{ validator: checkGoodsTemplateId, trigger: ['blur', 'change'] }],
        modelsIds: [{ required: true, message: '请选择规格型号', trigger: 'change' }],
        counts: [{ required: true, message: '请输入', trigger: 'change' }],
      },
@@ -247,7 +253,6 @@
        title: '上传',
        max: 20, // 最大大小,单位M
        num: 10, // 支持上传图片个数
        accept: '.jpg,.png', // 限制格式
        tip: '', // 提示 默认:`只能上传${this.defaultSettings.num}个${this.defaultSettings.accept}文件,且不超过${this.defaultSettings.max}kb`
        uploadUrl: getUploadUrl(), // 上传路径
        multiple: true, // 是否支持批量上传
@@ -304,7 +309,7 @@
    // 获取物品名称列表
    async getgoodsTemplate(id, index) {
      await goodsTemplate({ categoryId: id || '' }).then((res) => {
      await goodsTemplate({ agencyId: this.userInfo.tenantId, categoryId: id || '' }).then((res) => {
        if (index || index == 0) {
          this.$set(this.formData.goods[index], 'goodsOptions', res);
        } else {
@@ -350,10 +355,8 @@
      this.formData.goods[index].modelsOptions = [];
      this.formData.goods[index].modelsIds = [];
      this.formData.goods[index].models = [];
      this.formData.goods[index].baseCategoryId = e[e.length - 1];
      // 根据选中分类请求物品名称列表
      this.getgoodsTemplate(e[e.length - 1], index);
      this.getgoodsTemplate(e, index);
    },
    // 物品名称列表选择
@@ -390,6 +393,10 @@
    // 规格型号移除
    modelRemoveTag(e, index) {
      if(e===-1){
        this.formData.goods[index].models = []
        return
      }
      let arr = this.formData.goods[index].models;
      let delIndex = arr.findIndex((v) => v.baseGoodsModelsId == e);
      this.formData.goods[index].models.splice(delIndex, 1);
@@ -398,7 +405,7 @@
    // 上传
    uploadChange() {
      let arr = this.$refs.uploadRef.fileList;
      this.formData.procureDoc = JSON.stringify(arr);
      this.formData.doc = JSON.stringify(arr);
    },
    // 点击新增物品
admin-web/src/views/stock/index.scss
@@ -187,12 +187,13 @@
    cursor: pointer;
    .img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
  .goods-card {
    position: relative;
    background: #f6f6f6;
    padding: 20px;
    box-sizing: border-box;
    border-radius: 4px;
admin-web/src/views/stock/ledger/inventoryAlert/edit.vue
@@ -1,28 +1,31 @@
<template>
  <win-md :title="`${setting.title}预警设置`" @close="close" :width="'800px'">
  <win-md :title="`${setting.title}预警设置`" @close="close" :width="'800px'" :loading="loading">
    <el-form ref="ruleForm" :model="formData" :rules="rules" class="demo-ruleForm" label-width="100px">
      <el-row :gutter="24">
        <el-col :span="12">
          <el-form-item label="入库仓库" prop="baseWarehouseId">
          <el-form-item label="预警仓库" prop="baseWarehouseId">
            <el-select
              v-model="formData.baseWarehouseId"
              placeholder="请选择"
              style="width: 100%"
              :disabled="type == 'edit'"
                v-model="formData.baseWarehouseId"
                placeholder="请选择预警仓库"
                style="width: 100%"
                :disabled="type == 'edit'"
            >
              <el-option v-for="item in warehouses" :key="item.id" :label="item.warehouseName" :value="item.id" />
              <el-option v-for="item in warehouses" :key="item.id" :label="item.warehouseName" :value="item.id"/>
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="物品分类" prop="baseCategoryIds">
          <el-form-item label="物品分类" prop="baseCategoryId">
            <el-cascader
              v-model="formData.baseCategoryIds"
              :options="categoryOptions"
              :props="{ value: 'id' }"
              @change="categoryChange"
              style="width: 100%"
              :disabled="type == 'edit'"
                v-model="formData.baseCategoryId"
                :options="categoryOptions"
                :props="{ value: 'id',emitPath: false }"
                :show-all-levels="false"
                filterable
                clearable
                @change="categoryChange"
                style="width: 100%"
                :disabled="type == 'edit'"
            ></el-cascader>
          </el-form-item>
        </el-col>
@@ -31,18 +34,19 @@
        <el-col :span="12">
          <el-form-item label="物品名称" prop="baseGoodsTemplateId">
            <el-select
              :value="formData.baseGoodsTemplateId"
              placeholder="请先择物品分类"
              filterable
              :disabled="!formData.baseCategoryId || type == 'edit'"
              @change="goodsTemplateChange"
              style="width: 100%"
                :value="formData.baseGoodsTemplateId"
                :placeholder="formData.baseCategoryId?'请选择物品':'请先选择物品分类'"
                filterable
                clearable
                :disabled="!formData.baseCategoryId || type == 'edit'"
                @change="goodsTemplateChange"
                style="width: 100%"
            >
              <el-option
                v-for="item in goodsTemplatelOptions"
                :key="item.id"
                :label="item.goodsName"
                :value="item.id"
                  v-for="item in goodsTemplatelOptions"
                  :key="item.id"
                  :label="item.goodsName"
                  :value="item.id"
              />
            </el-select>
          </el-form-item>
@@ -50,15 +54,17 @@
        <el-col :span="12">
          <el-form-item label="规格型号" prop="modelsIds">
            <el-select
              v-model="formData.modelsIds"
              multiple
              :disabled="!formData.baseGoodsTemplateId || type == 'edit'"
              placeholder="请先择物品名称"
              @change="modelChange"
              @remove-tag="modelRemoveTag"
              style="width: 100%"
                v-model="formData.modelsIds"
                multiple
                clearable
                :disabled="!formData.baseGoodsTemplateId || type == 'edit'"
                :placeholder="formData.baseCategoryId?'请选择规格型号':'请先选择物品名称'"
                @change="modelChange"
                @remove-tag="modelRemoveTag"
                @clear="modelRemoveTag(-1)"
                style="width: 100%"
            >
              <el-option v-for="item in modelsOptions" :key="item.id" :label="item.modelName" :value="item.id" />
              <el-option v-for="item in modelsOptions" :key="item.id" :label="item.modelName" :value="item.id"/>
            </el-select>
          </el-form-item>
        </el-col>
@@ -84,10 +90,10 @@
        <el-table-column prop="lowerLimit" label="保底库存" align="center">
          <template slot-scope="scope">
            <el-form-item
              v-if="scope.row.lowerLimit || scope.row.upperLimit"
              label-width="0"
              :prop="`models[${scope.$index}].lowerLimit`"
              :rules="rules.lowerLimit"
                v-if="scope.row.lowerLimit || scope.row.upperLimit"
                label-width="0"
                :prop="`models[${scope.$index}].lowerLimit`"
                :rules="rules.lowerLimit"
            >
              <el-input v-model.number="scope.row.lowerLimit"></el-input>
            </el-form-item>
@@ -97,10 +103,10 @@
        <el-table-column prop="upperLimit" label="封顶库存" align="center">
          <template slot-scope="scope">
            <el-form-item
              v-if="scope.row.lowerLimit || scope.row.upperLimit"
              label-width="0"
              :prop="`models[${scope.$index}].upperLimit`"
              :rules="rules.upperLimit"
                v-if="scope.row.lowerLimit || scope.row.upperLimit"
                label-width="0"
                :prop="`models[${scope.$index}].upperLimit`"
                :rules="rules.upperLimit"
            >
              <el-input v-model.number="scope.row.upperLimit"></el-input>
            </el-form-item>
@@ -110,8 +116,8 @@
      </el-table>
    </el-form>
    <div slot="footer" align="center" class="dialog-footer">
      <my-button name="取消" site="form" @click="close" />
      <my-button name="保存" site="form" @click="save" />
      <my-button name="取消" site="form" @click="close"/>
      <my-button name="保存" site="form" @click="save"/>
    </div>
  </win-md>
</template>
@@ -131,15 +137,17 @@
import {findParentIds} from '@/utils/index';
export default {
  components: { winMd, myButton },
  components: {winMd, myButton},
  props: {
    setting: {
      type: Object,
      default: () => {},
      default: () => {
      },
    },
  },
  data() {
    return {
      loading: true,
      type: '',
      warehouses: [], // 入库仓库列表
      agencyOptions: [], // 调拨机构
@@ -156,16 +164,17 @@
        models: [],
      },
      rules: {
        baseCategoryIds: [{ required: true, message: '请选择', trigger: 'blur' }],
        baseGoodsTemplateId: [{ required: true, message: '请选择', trigger: 'blur' }],
        modelsIds: [{ required: true, message: '请选择', trigger: 'blur' }],
        baseWarehouseId: [{required: true, message: '请选择预警仓库', trigger: 'change'}],
        baseCategoryId: [{required: true, message: '请选择物品分类', trigger: 'change'}],
        baseGoodsTemplateId: [{required: true, message: '请选择物品', trigger: 'change'}],
        modelsIds: [{required: true, message: '请选择规格型号', trigger: 'change'}],
        lowerLimit: [
          { required: true, message: '请输入', trigger: 'blur' },
          { type: 'number', message: '请输入数字值', trigger: 'blur' },
          {required: true, message: '请输入', trigger: 'blur'},
          {type: 'number', message: '请输入数字值', trigger: 'blur'},
        ],
        upperLimit: [
          { required: true, message: '请输入', trigger: 'blur' },
          { type: 'number', message: '请输入数字值', trigger: 'blur' },
          {required: true, message: '请输入', trigger: 'blur'},
          {type: 'number', message: '请输入数字值', trigger: 'blur'},
        ],
      },
    };
@@ -178,13 +187,14 @@
  },
  methods: {
    async init() {
      await this.getWarehouseList();
      await this.getCategoryTree();
      if (this.setting.id) {
        this.type = 'edit';
        const detail = await warningConfigGetById({ id: this.setting.id });
        await this.getCategoryTree();
        const detail = await warningConfigGetById({id: this.setting.id});
        this.formData = Object.assign({}, detail);
        // 分类反显
        let res = await queryGoodsModelInfo({ baseGoodsModelsId: this.formData.baseGoodsModelsId });
        let res = await queryGoodsModelInfo({baseGoodsModelsId: this.formData.baseGoodsModelsId});
        this.formData.baseCategoryId = res[0].categoryId;
        this.formData.baseCategoryIds = findParentIds(this.categoryOptions, this.formData.baseCategoryId);
        // 物品名称反显
@@ -193,24 +203,22 @@
        this.getgoodsModel(this.formData.baseGoodsTemplateId);
        this.formData.modelsIds = [this.formData.baseGoodsModelsId];
        this.getWarehouseList();
      } else {
        this.getWarehouseList();
        this.getCategoryTree();
      }
      this.loading = false
    },
    // 获取入库仓库列表
    getWarehouseList() {
      selectTenantWarehouse({ agencyId: this.userInfo.tenantId })
        .then((res) => {
          this.warehouses = res;
          if (this.warehouses.length && !this.formData.baseWarehouseId) {
            // 默认选中第一个仓库
            this.formData.baseWarehouseId = this.warehouses[0].id;
          }
        })
        .catch((err) => {
          console.log('err', err);
        });
      selectTenantWarehouse({agencyId: this.userInfo.tenantId})
          .then((res) => {
            this.warehouses = res;
            if (this.warehouses.length && !this.formData.baseWarehouseId) {
              // 默认选中第一个仓库
              this.formData.baseWarehouseId = this.warehouses[0].id;
            }
          })
          .catch((err) => {
            console.log('err', err);
          });
    },
    getCategoryTree() {
@@ -224,14 +232,14 @@
    // 获取物品名称列表
    getgoodsTemplate(id) {
      goodsTemplate({ categoryId: id || '', agencyId: this.userInfo.tenantId }).then((res) => {
      goodsTemplate({categoryId: id || '', agencyId: this.userInfo.tenantId}).then((res) => {
        this.goodsTemplatelOptions = res;
      });
    },
    // 规格型号
    getgoodsModel(id) {
      goodsModel({ goodsTemplatesId: id || '' }).then((res) => {
      goodsModel({goodsTemplatesId: id || ''}).then((res) => {
        this.modelsOptions = res;
      });
    },
@@ -247,23 +255,19 @@
    // 物品分类选择
    categoryChange(e) {
      if (!e) return;
      this.goodsTemplatelOptions = [];
      this.modelsOptions = [];
      this.formData.baseGoodsTemplateId = '';
      this.formData.modelsIds = [];
      this.formData.models = [];
      this.formData.baseCategoryId = e[e.length - 1];
      // 根据选中分类请求物品名称列表
      this.getgoodsTemplate(this.formData.baseCategoryId);
      this.getgoodsTemplate(e);
    },
    // 物品名称列表选择
    goodsTemplateChange(e) {
      this.formData.modelsIds = [];
      this.formData.models = [];
      this.formData.baseGoodsTemplateId = e;
      // 根据选中物品名称id获取规格型号列表
      this.getgoodsModel(e);
@@ -286,24 +290,31 @@
      let str = JSON.stringify(arr);
      e.forEach((item, index) => {
        if (str && !str.includes(item)) {
          arr.push({ baseGoodsModelsId: item, lowerLimit: null, upperLimit: null });
          arr.push({baseGoodsModelsId: item, lowerLimit: null, upperLimit: null});
        }
      });
    },
    // 规格型号移除
    modelRemoveTag(e) {
      if (e === -1) {
        this.formData.models = []
        return
      }
      let delIndex = this.formData.models.findIndex((v) => v.baseGoodsModelsId == e);
      this.formData.models.splice(delIndex, 1);
    },
    getEditInfo(id) {},
    getEditInfo(id) {
    },
    close() {
      this.$emit('close');
    },
    save() {
      this.$refs.ruleForm.validate((valid) => {
        if (valid) {
          if (this.loading) return
          this.loading = true;
          if (this.setting.id) {
            // 编辑接口
            const params = {
@@ -312,9 +323,13 @@
              upperLimit: this.formData.upperLimit,
            };
            warningConfigUpd(params).then((res) => {
              this.loading = false;
              this.$message.success('保存成功!');
              this.close();
              this.$emit('search');
            }).catch((err) => {
              this.loading = false;
              this.$message.error('保存失败');
            });
          } else {
            const params = {
@@ -324,9 +339,13 @@
              modelConfigStr: JSON.stringify(this.formData.models),
            };
            warningConfigAdd(params).then((res) => {
              this.loading = false;
              this.$message.success('保存成功!');
              this.close();
              this.$emit('search');
            }).catch((err) => {
              this.loading = false;
              this.$message.error('保存失败');
            });
          }
        } else {
admin-web/src/views/stock/procure/purchaseOrder/edit.vue
@@ -5,7 +5,7 @@
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="12">
            <el-form-item label="入库仓库" prop="warehouseId">
              <el-select v-model="formData.warehouseId" placeholder="请选择" style="width: 100%">
              <el-select v-model="formData.warehouseId" placeholder="请选择" clearable style="width: 100%">
                <el-option v-for="item in warehouses" :key="item.id" :label="item.warehouseName" :value="item.id"/>
              </el-select>
            </el-form-item>
@@ -15,6 +15,7 @@
              <el-date-picker
                  v-model="formData.procureTime"
                  type="datetime"
                  clearable
                  value="yyyy-MM-dd HH:mm:ss"
                  value-format="yyyyMMddHHmmss"
                  placeholder="请选择日期"
@@ -45,17 +46,20 @@
            <el-col :span="12">
              <el-form-item
                  label="物品分类"
                  :prop="`procureGoods[${goodsIndex}].baseCategoryIds`"
                  :prop="`procureGoods.${goodsIndex}.baseCategoryId`"
                  :rules="{
                  required: true,
                  message: '请选择',
                  message: '请选择物品分类',
                  trigger: 'change',
                }"
              >
                <el-cascader
                    v-model="goodsItem.baseCategoryIds"
                    v-model="goodsItem.baseCategoryId"
                    :options="categoryOptions"
                    :props="{ value: 'id' }"
                    :props="{ value: 'id',emitPath: false }"
                    :show-all-levels="false"
                    filterable
                    clearable
                    @change="categoryChange($event, goodsIndex)"
                    style="width: 100%"
                ></el-cascader>
@@ -64,13 +68,14 @@
            <el-col :span="12">
              <el-form-item
                  label="物品名称"
                  :prop="`procureGoods[${goodsIndex}].baseGoodsTemplateId`"
                  :prop="`procureGoods.${goodsIndex}.baseGoodsTemplateId`"
                  :rules="rules.baseGoodsTemplateId"
              >
                <el-select
                    v-model="goodsItem.baseGoodsTemplateId"
                    placeholder="请先择物品分类"
                    :placeholder="goodsItem.baseCategoryId?'请选择物品':'请先选择物品分类'"
                    filterable
                    clearable
                    :disabled="!goodsItem.baseCategoryId"
                    style="width: 100%"
                    @change="goodsTemplateChange($event, goodsIndex)"
@@ -89,20 +94,22 @@
            <el-col :span="12">
              <el-form-item
                  label="规格型号"
                  :prop="`procureGoods[${goodsIndex}].modelsIds`"
                  :prop="`procureGoods.${goodsIndex}.modelsIds`"
                  :rules="{
                  required: true,
                  message: '请选择',
                  message: '请选择规格型号',
                  trigger: 'change',
                }"
              >
                <el-select
                    v-model="goodsItem.modelsIds"
                    multiple
                    placeholder="请先择物品名称"
                    clearable
                    :placeholder="goodsItem.baseCategoryId?'请选择规格型号':'请先选择物品名称'"
                    :disabled="!goodsItem.baseCategoryId"
                    @change="modelChange($event, goodsIndex)"
                    @remove-tag="modelRemoveTag($event, goodsIndex)"
                    @clear="modelRemoveTag(-1, goodsIndex)"
                    style="width: 100%"
                >
                  <el-option
@@ -305,7 +312,7 @@
        procureDoc: [{required: true, message: '请上传', trigger: 'change'}],
        baseCategoryIds: [{required: true, message: '请选择', trigger: 'change'}],
        baseGoodsTemplateId: [{validator: checkGoodsTemplateId, trigger: ['blur', 'change']}],
        baseGoodsTemplateId: [{required: true, message: '请选择', trigger: 'change'},{validator: checkGoodsTemplateId, trigger: ['blur', 'change']}],
        modelsIds: [{required: true, message: '请选择', trigger: 'change'}],
        priceYuan: [{validator: checkPrice, trigger: 'blur'}],
        counts: [{validator: checkCounts, trigger: 'blur'}],
@@ -332,6 +339,8 @@
  methods: {
    async init() {
      await this.getWarehouseList();
      await this.getgoodsTemplate();
      await this.getgoodsModel();
      // 获取物品分类列表
      const treeRes = await getTree();
@@ -359,9 +368,6 @@
          });
          return item;
        });
      } else {
        await this.getgoodsTemplate();
        await this.getgoodsModel();
      }
      this.loading = false;
    },
@@ -397,7 +403,7 @@
    // 获取物品名称列表
    async getgoodsTemplate(id, index) {
      await goodsTemplate({categoryId: id || ''}).then((res) => {
      await goodsTemplate({agencyId: this.userInfo.tenantId, categoryId: id || ''}).then((res) => {
        if (index || index == 0) {
          this.$set(this.formData.procureGoods[index], 'goodsOptions', res);
        } else {
@@ -444,9 +450,8 @@
      this.formData.procureGoods[index].modelsIds = [];
      this.formData.procureGoods[index].models = [];
      this.formData.procureGoods[index].baseCategoryId = e[e.length - 1];
      // 根据选中分类请求物品名称列表
      this.getgoodsTemplate(e[e.length - 1], index);
      this.getgoodsTemplate(e, index);
    },
    // 物品名称列表
@@ -475,6 +480,10 @@
    // 规格型号移除
    modelRemoveTag(e, index) {
      if(e===-1){
        this.formData.procureGoods[index].models = []
        return
      }
      let arr = this.formData.procureGoods[index].models;
      let delIndex = arr.findIndex((v) => v.baseGoodsModelsId == e);
      this.formData.procureGoods[index].models.splice(delIndex, 1);
admin-web/src/views/stock/scrap/itemScrapping/detail.vue
@@ -1,5 +1,5 @@
<template>
  <win-md class="stock-detail" :title="setting.title" @close="close" :width="'800px'">
  <win-md class="stock-detail" :title="setting.title" @close="close" :width="'800px'" :loading="loading">
    <el-row :gutter="20">
      <el-col :span="8">
        <span>报废单号:</span>
@@ -85,6 +85,7 @@
  },
  data() {
    return {
      loading: true,
      scrapReasonOptions: [],
      fileList: [],
      detail: {
@@ -110,6 +111,7 @@
  created() {
    scrappedDetail({ id: this.setting.id }).then((res) => {
      this.detail = res;
      this.loading = false
      this.fileList = this.detail.uploadFiles ? JSON.parse(this.detail.uploadFiles) : [];
      this.$nextTick(() => {
        this.initPreviewImg();
admin-web/src/views/stock/scrap/itemScrapping/edit.vue
@@ -1,6 +1,6 @@
<template>
  <win-md class="stock-edit" :title="`${setting.title}报废`" @close="close" :width="'800px'">
    <el-form class="form" ref="ruleForm" :model="formData" :rules="rules" label-width="120px">
    <el-form v-loading="loading" class="form" ref="ruleForm" :model="formData" :rules="rules" label-width="120px">
      <div class="main-w">
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="12">
@@ -26,6 +26,7 @@
            <el-form-item label="报废时间" prop="dealTime">
              <el-date-picker
                v-model="formData.dealTime"
                clearable
                type="datetime"
                value="yyyy-MM-dd HH:mm:ss"
                value-format="yyyyMMddHHmmss"
@@ -48,17 +49,20 @@
            <el-col :span="12">
              <el-form-item
                label="物品分类"
                :prop="`scrappedGoodsInfo[${goodsIndex}].baseCategoryIds`"
                :prop="`scrappedGoodsInfo.${goodsIndex}.baseCategoryId`"
                :rules="{
                  required: true,
                  message: '请选择',
                  message: '请选择物品分类',
                  trigger: 'change',
                }"
              >
                <el-cascader
                  v-model="goodsItem.baseCategoryIds"
                  v-model="goodsItem.baseCategoryId"
                  :options="categoryOptions"
                  :props="{ value: 'id' }"
                  :props="{ value: 'id',emitPath: false }"
                  :show-all-levels="false"
                  filterable
                  clearable
                  @change="categoryChange($event, goodsIndex)"
                  style="width: 100%"
                ></el-cascader>
@@ -67,13 +71,14 @@
            <el-col :span="12">
              <el-form-item
                label="物品名称"
                :prop="`scrappedGoodsInfo[${goodsIndex}].baseGoodsTemplateId`"
                :prop="`scrappedGoodsInfo.${goodsIndex}.baseGoodsTemplateId`"
                :rules="rules.baseGoodsTemplateId"
              >
                <el-select
                  v-model="goodsItem.baseGoodsTemplateId"
                  placeholder="请先择物品分类"
                  :placeholder="goodsItem.baseCategoryId?'请选择物品':'请先选择物品分类'"
                  filterable
                  clearable
                  :disabled="!goodsItem.baseCategoryId"
                  style="width: 100%"
                  @change="goodsTemplateChange($event, goodsIndex)"
@@ -95,17 +100,19 @@
                :prop="`scrappedGoodsInfo[${goodsIndex}].modelsIds`"
                :rules="{
                  required: true,
                  message: '请选择',
                  message: '请选择规格型号',
                  trigger: 'change',
                }"
              >
                <el-select
                  v-model="goodsItem.modelsIds"
                  multiple
                  placeholder="请先择物品名称"
                  clearable
                  :placeholder="goodsItem.baseCategoryId?'请选择规格型号':'请先选择物品名称'"
                  :disabled="!goodsItem.baseCategoryId"
                  @change="modelChange($event, goodsIndex)"
                  @remove-tag="modelRemoveTag($event, goodsIndex)"
                  @clear="modelRemoveTag(-1, goodsIndex)"
                  style="width: 100%"
                >
                  <el-option
@@ -209,7 +216,7 @@
      callback();
    };
    return {
      loading: false,
      loading: true,
      fileList: [],
      warehouses: [], // 报废仓库列表
      categoryOptions: [], // 物品分类列表
@@ -236,8 +243,8 @@
        dealTime: [{ required: true, message: '请选择', trigger: 'change' }],
        buyType: [{ required: true, message: '请选择', trigger: 'change' }],
        uploadFiles: [{ required: true, message: '请上传', trigger: 'change' }],
        baseCategoryIds: [{ required: true, message: '请选择', trigger: 'change' }],
        baseGoodsTemplateId: [{ validator: checkGoodsTemplateId, trigger: ['blur', 'change'] }],
        baseCategoryId: [{ required: true, message: '请选择', trigger: 'change' }],
        baseGoodsTemplateId: [{ required: true, message: '请选择', trigger: 'change' },{ validator: checkGoodsTemplateId, trigger: ['blur', 'change'] }],
        modelsIds: [{ required: true, message: '请选择', trigger: 'change' }],
      },
@@ -261,9 +268,7 @@
  },
  methods: {
    async init() {
      this.getWarehouseList();
      this.getgoodsTemplate();
      this.getgoodsModel();
      await this.getWarehouseList();
      await this.getCategoryTree();
      this.formData.scrappedGoodsInfo.push(JSON.parse(JSON.stringify(this.goodsItem)));
      this.formData.operatorId = this.userInfo.id;
@@ -271,6 +276,9 @@
      this.formData.agencyId = this.userInfo.tenantId;
      this.formData.agencyName = this.userInfo.tenantName;
      this.formData.warehouseType = 0;
      this.getgoodsTemplate();
      this.getgoodsModel();
      this.loading = false
      getDicts('SCRAP_REASON').then((res) => {
        this.scrapReasonOptions = res;
      });
@@ -282,8 +290,8 @@
    },
    // 获取报废仓库列表
    getWarehouseList() {
      selectTenantWarehouse({ agencyId: this.userInfo.tenantId })
    async getWarehouseList() {
      await selectTenantWarehouse({ agencyId: this.userInfo.tenantId })
        .then((res) => {
          this.warehouses = res;
          if (this.warehouses.length && !this.formData.warehouseId) {
@@ -309,7 +317,7 @@
    // 获取物品名称列表
    getgoodsTemplate(id, index) {
      goodsTemplate({ categoryId: id || '' }).then((res) => {
      goodsTemplate({ agencyId: this.userInfo.tenantId, categoryId: id || '' }).then((res) => {
        if (index || index == 0) {
          this.$set(this.formData.scrappedGoodsInfo[index], 'goodsOptions', res);
        } else {
@@ -355,10 +363,8 @@
      this.formData.scrappedGoodsInfo[index].modelsOptions = [];
      this.formData.scrappedGoodsInfo[index].modelsIds = [];
      this.formData.scrappedGoodsInfo[index].scrappedGoodsList = [];
      this.formData.scrappedGoodsInfo[index].baseCategoryId = e[e.length - 1];
      // 根据选中分类请求物品名称列表
      this.getgoodsTemplate(e[e.length - 1], index);
      this.getgoodsTemplate(e, index);
    },
    // 物品名称列表
@@ -394,6 +400,10 @@
    // 规格型号移除
    modelRemoveTag(e, index) {
      if(e===-1){
        this.formData.scrappedGoodsInfo[index].scrappedGoodsList = []
        return
      }
      let arr = this.formData.scrappedGoodsInfo[index].scrappedGoodsList;
      let delIndex = arr.findIndex((v) => v.baseGoodsModelsId == e);
      this.formData.scrappedGoodsInfo[index].scrappedGoodsList.splice(delIndex, 1);
@@ -419,13 +429,17 @@
    handleSubmit() {
      this.$refs['ruleForm'].validate((valid) => {
        if (valid) {
          if (this.loading) return
          this.loading = true;
          scrappedAdd(this.formData)
            .then((res) => {
              this.loading = false;
              this.$message.success('保存成功!');
              this.close();
              this.$emit('search');
            })
            .catch((err) => {
              this.loading = false;
              console.log('create err', err);
              this.$message.error('保存失败');
            });
admin-web/src/views/stock/transfer/transferApplication/detail.vue
@@ -1,76 +1,87 @@
<template>
  <win-md class="stock-detail" :title="setting.title" @close="close" :width="'800px'" :loading="loading">
    <div v-loading="loading">
      <el-row :gutter="20">
        <el-col :span="8">
          <span>调拨单号:</span>
          <span>{{ detail.businessFormCode }}</span>
        </el-col>
        <el-col :span="8">
          <span>调拨机构:</span>
          <span>{{ detail.outAgencyName }}</span>
        </el-col>
        <el-col :span="8">
          <span>接收机构:</span>
          <span>{{ detail.inAgencyName }}</span>
        </el-col>
      </el-row>
      <el-row :gutter="20" style="margin-top: 20px">
        <el-col :span="8">
          <span>申请调拨时间:</span>
          <span>{{ detail.createTime | formatTime }}</span>
        </el-col>
        <el-col :span="8">
          <span>状态:</span>
          <span>{{ getStatesLabel(detail.states) }}</span>
        </el-col>
        <el-col :span="8">
          <span>调拨时间:</span>
          <span>{{ detail.outputTime | formatTime }}</span>
        </el-col>
      </el-row>
      <el-row v-if="fileList && fileList.length" :gutter="20" style="margin-top: 20px">
        <el-col class="img-row" :span="24">
          <span>调拨手续照片:</span>
    <el-row :gutter="20">
      <el-col :span="8">
        <span>调拨单号:</span>
        <span>{{ detail.businessFormCode }}</span>
      </el-col>
      <el-col :span="8">
        <span>调拨机构:</span>
        <span>{{ detail.outAgencyName }}</span>
      </el-col>
      <el-col :span="8">
        <span>接收机构:</span>
        <span>{{ detail.inAgencyName }}</span>
      </el-col>
    </el-row>
    <el-row :gutter="20" style="margin-top: 20px">
      <el-col :span="8">
        <span>申请调拨时间:</span>
        <span>{{ detail.createTime | formatTime }}</span>
      </el-col>
      <el-col :span="8">
        <span>状态:</span>
        <span>{{ getStatesLabel(detail.states) }}</span>
      </el-col>
      <el-col :span="8">
        <span>调拨时间:</span>
        <span>{{ detail.outputTime | formatTime }}</span>
      </el-col>
    </el-row>
    <el-row v-if="fileList && fileList.length" :gutter="20" style="margin-top: 20px">
      <el-col class="img-row" :span="24">
        <span>调拨手续照片:</span>
        <div class="img-box" v-for="(item, index) in fileList" :key="index" @click="handlePreview(item)">
          <img class="img" :src="getUrl(item.path)" alt="" />
          <img class="img" :src="getUrl(item.path)" alt=""/>
        </div>
      </el-col>
    </el-row>
    <div class="goods-card" v-for="(goodsItem, goodsIndex) in detail.formTransferGoods" :key="goodsIndex">
      <el-row :gutter="20">
        <el-col :span="12">
          <span>物品分类:</span>
          <span>{{ goodsItem.categoryName }}</span>
        </el-col>
        <el-col :span="12">
          <span>物品名称:</span>
          <span>{{ goodsItem.goodsName }}</span>
        </el-col>
      </el-row>
      <div class="goods-card" v-for="(goodsItem, goodsIndex) in detail.formTransferGoods" :key="goodsIndex">
        <el-row :gutter="20">
          <el-col :span="12">
            <span>物品分类:</span>
            <span>{{ goodsItem.categoryName }}</span>
          </el-col>
          <el-col :span="12">
            <span>物品名称:</span>
            <span>{{ goodsItem.goodsName }}</span>
          </el-col>
        </el-row>
        <el-table :data="goodsItem.models" :stripe="true" style="margin-top: 20px">
          <el-table-column prop="baseGoodsModelsName" label="规格型号" align="center">
            <template slot-scope="scope">
              {{ scope.row.baseGoodsModelsName }}
            </template>
          </el-table-column>
          <el-table-column label="单位" align="center">
            <template slot-scope="scope">
              {{ scope.row.unit }}
            </template>
          </el-table-column>
          <el-table-column prop="counts" label="调拨数量" align="center">
            <template slot-scope="scope">
              {{ scope.row.counts }}
            </template>
          </el-table-column>
          <el-table-column prop="price" label="金额" align="center">
            <template slot-scope="scope">
              {{ scope.row.price }}
            </template>
          </el-table-column>
        </el-table>
      </div>
      <el-table :data="goodsItem.models" :stripe="true" style="margin-top: 20px">
        <el-table-column prop="baseGoodsModelsName" label="规格型号" align="center">
          <template slot-scope="scope">
            {{ scope.row.baseGoodsModelsName }}
          </template>
        </el-table-column>
        <el-table-column label="单位" align="center">
          <template slot-scope="scope">
            {{ scope.row.unit }}
          </template>
        </el-table-column>
        <el-table-column prop="counts" label="调拨数量" align="center">
          <template slot-scope="scope">
            {{ scope.row.counts }}
          </template>
        </el-table-column>
        <el-table-column prop="price" label="金额" align="center">
          <template slot-scope="scope">
            {{ scope.row.price }}
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div id="uploadPreviewImages" style="display: none">
      <span v-for="(src, index) in fileList" :key="index">
        <img
            v-if="checkImg(src.name)"
            class="v-img"
            :src="src.url"
            :alt="src.name"
            style="width: 100px; height: 100px"
        />
      </span>
    </div>
  </win-md>
</template>
@@ -79,21 +90,28 @@
import * as DateFormatter from '@/utils/DateFormatter';
import winMd from '@/components/win/win-md';
import transfer from '../../../mixins/transfer';
import {getDownUrl} from "@/utils/base";
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
let viewer = null;
export default {
  mixins: [transfer],
  components: { winMd },
  components: {winMd},
  props: {
    setting: {
      type: Object,
      default: () => {},
      default: () => {
      },
    },
  },
  data() {
    return {
      loading: true,
      detail: {},
      fileList:[]
      fileList: []
    };
  },
  filters: {
@@ -103,13 +121,54 @@
    },
  },
  created() {
    transferDetail({ id: this.setting.id }).then((res) => {
    transferDetail({id: this.setting.id}).then((res) => {
      this.detail = res;
      this.fileList = this.detail.procureDoc ? JSON.parse(this.detail.procureDoc) : [];
      this.loading = false;
      this.$nextTick(() => {
        this.initPreviewImg();
      });
    });
  },
  methods: {
    initPreviewImg() {
      if (viewer != null) {
        viewer.destroy();
      }
      const ViewerDom = document.querySelector('#uploadPreviewImages');
      viewer = new Viewer(ViewerDom, {});
    },
    handlePreview(file) {
      if (!this.checkImg(file.name)) {
        return false;
      }
      let index = 0;
      for (let i = 0; i < this.fileList.length; i++) {
        const f = this.fileList[i];
        if (this.checkImg(f.name)) {
          if (file.id == f.id) {
            break;
          }
          index++;
        }
      }
      viewer.view(index);
    },
    checkImg(name) {
      const suffix = name.substring(name.lastIndexOf('.'), name.length);
      const imgArray = ['.jpg', '.jpeg', '.png', '.bmp'];
      if (imgArray.indexOf(suffix) < 0) {
        return false;
      }
      return true;
    },
    getUrl(path) {
      if (path.substr(0, 7).toLowerCase() == 'http://' || path.substr(0, 8).toLowerCase() == 'https://') {
        return path;
      } else {
        return getDownUrl() + path;
      }
    },
    close() {
      this.$emit('close');
    },
admin-web/src/views/stock/transfer/transferApplication/edit.vue
@@ -1,6 +1,6 @@
<template>
  <win-md class="stock-edit" title="调拨申请" @close="close" :width="'800px'">
    <el-form class="form" ref="ruleForm" :model="formData" :rules="rules" label-width="120px">
    <el-form v-loading="loading" class="form" ref="ruleForm" :model="formData" :rules="rules" label-width="120px">
      <div class="main-w">
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="12">
@@ -11,10 +11,11 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="调拨时间" prop="createTime">
            <el-form-item label="调拨时间" clearable prop="createTime">
              <el-date-picker
                v-model="formData.createTime"
                type="datetime"
                clearable
                value="yyyy-MM-dd HH:mm:ss"
                value-format="yyyyMMddHHmmss"
                placeholder="请选择日期"
@@ -27,7 +28,7 @@
        <el-row :gutter="24" class="headerHeight">
          <el-col :span="24">
            <el-form-item label="调拨手续" prop="procureDoc">
              <upload :settings="uploadSettings" @on-change="uploadChange"></upload>
              <upload ref="uploadRef" :settings="uploadSettings" @on-change="uploadChange"></upload>
            </el-form-item>
          </el-col>
        </el-row>
@@ -36,17 +37,20 @@
            <el-col :span="12">
              <el-form-item
                label="物品分类"
                :prop="`transferGoods[${goodsIndex}].baseCategoryIds`"
                :prop="`transferGoods.${goodsIndex}.baseCategoryId`"
                :rules="{
                  required: true,
                  message: '请选择',
                  message: '请选择物品分类',
                  trigger: 'change',
                }"
              >
                <el-cascader
                  v-model="goodsItem.baseCategoryIds"
                  v-model="goodsItem.baseCategoryId"
                  :options="categoryOptions"
                  :props="{ value: 'id' }"
                  :props="{ value: 'id',emitPath: false }"
                  :show-all-levels="false"
                  filterable
                  clearable
                  @change="categoryChange($event, goodsIndex)"
                  style="width: 100%"
                ></el-cascader>
@@ -55,13 +59,14 @@
            <el-col :span="12">
              <el-form-item
                label="物品名称"
                :prop="`transferGoods[${goodsIndex}].baseGoodsTemplateId`"
                :prop="`transferGoods.${goodsIndex}.baseGoodsTemplateId`"
                :rules="rules.baseGoodsTemplateId"
              >
                <el-select
                  v-model="goodsItem.baseGoodsTemplateId"
                  placeholder="请先择物品分类"
                  :placeholder="goodsItem.baseCategoryId?'请选择物品':'请先选择物品分类'"
                  filterable
                  clearable
                  :disabled="!goodsItem.baseCategoryId"
                  style="width: 100%"
                  @change="goodsTemplateChange($event, goodsIndex)"
@@ -90,10 +95,12 @@
                <el-select
                  v-model="goodsItem.modelsIds"
                  multiple
                  placeholder="请先择物品名称"
                  clearable
                  :placeholder="goodsItem.baseCategoryId?'请选择规格型号':'请先选择物品名称'"
                  :disabled="!goodsItem.baseCategoryId"
                  @change="modelChange($event, goodsIndex)"
                  @remove-tag="modelRemoveTag($event, goodsIndex)"
                  @clear="modelRemoveTag(-1, goodsIndex)"
                  style="width: 100%"
                >
                  <el-option
@@ -199,12 +206,13 @@
      callback();
    };
    return {
      loading: false,
      loading: true,
      agencyOptions: [], // 调拨机构
      categoryOptions: [], // 物品分类列表
      modelList: [], //型号列表
      formData: {
        transferBusinessType: 0, // 单据类型。0仓库调拨;1部门分发;2部门物品回退
        procureDoc: '',
        outAgencyId: '', // 调拨机构id
        createTime: '', // 调拨时间
        transferGoods: [],
@@ -228,9 +236,9 @@
      rules: {
        outAgencyId: [{ required: true, message: '请选择', trigger: 'change' }],
        createTime: [{ required: true, message: '请选择', trigger: 'change' }],
        procureDoc: [{ required: true, message: '请上传调拨手续', trigger: 'change' }],
        baseCategoryIds: [{ required: true, message: '请选择', trigger: 'change' }],
        baseGoodsTemplateId: [{ validator: checkGoodsTemplateId, trigger: ['blur', 'change'] }],
        baseGoodsTemplateId: [{ required: true, message: '请选择', trigger: 'change' },{ validator: checkGoodsTemplateId, trigger: ['blur', 'change'] }],
        modelsIds: [{ required: true, message: '请选择', trigger: 'change' }],
        counts: [
          { required: true, message: '请输入', trigger: 'change' },
@@ -242,12 +250,11 @@
        title: '上传',
        max: 20, // 最大大小,单位M
        num: 2, // 支持上传图片个数
        accept: '.jpg,.png', // 限制格式
        tip: '', // 提示 默认:`只能上传${this.defaultSettings.num}个${this.defaultSettings.accept}文件,且不超过${this.defaultSettings.max}kb`
        uploadUrl: getUploadUrl(), // 上传路径
        multiple: true, // 是否支持批量上传
        disabled: false, // 是否禁用
        type: 'text', // text/picture
        type: 'picture', // text/picture
      },
    };
  },
@@ -259,15 +266,16 @@
  },
  methods: {
    async init() {
      this.handegetParentTenant();
      await this.handegetParentTenant();
      await this.getCategoryTree();
      this.loading = false
      this.getgoodsTemplate();
      this.getgoodsModel();
      this.getCategoryTree();
    },
    // 获取上级机构
    handegetParentTenant() {
      getParentTenant().then((res) => {
    async handegetParentTenant() {
      await getParentTenant().then((res) => {
        if (res.id != this.userInfo.tenantId) {
          this.agencyOptions = [res];
          this.formData.outAgencyId = res.id;
@@ -343,10 +351,8 @@
      this.formData.transferGoods[index].modelsOptions = [];
      this.formData.transferGoods[index].modelsIds = [];
      this.formData.transferGoods[index].models = [];
      this.formData.transferGoods[index].baseCategoryId = e[e.length - 1];
      // 根据选中分类请求物品名称列表
      this.getgoodsTemplate(e[e.length - 1], index);
      this.getgoodsTemplate(e, index);
    },
    // 物品名称列表选择
@@ -382,6 +388,10 @@
    // 规格型号移除
    modelRemoveTag(e, index) {
      if(e===-1){
        this.formData.transferGoods[index].models = []
        return
      }
      let arr = this.formData.transferGoods[index].models;
      let delIndex = arr.findIndex((v) => v.baseGoodsModelsId == e);
      this.formData.transferGoods[index].models.splice(delIndex, 1);
@@ -389,7 +399,8 @@
    // 上传
    uploadChange(e) {
      console.log('uploadChange', e);
      let arr = this.$refs.uploadRef.fileList;
      this.formData.procureDoc = JSON.stringify(arr);
    },
    // 点击新增物品
@@ -420,14 +431,17 @@
    handleSubmit() {
      this.$refs['ruleForm'].validate((valid) => {
        if (valid) {
          console.log('this.formData', this.formData);
          if(this.loading) return
          this.loading = true
          transferAdd(this.formData)
            .then((res) => {
              this.loading = false
              this.$message.success('保存成功!');
              this.close();
              this.$emit('search');
            })
            .catch((err) => {
              this.loading = false
              console.log('edit err', err);
              this.$message.error('保存失败');
            });
admin-web/src/views/stock/transfer/transferissue/detail.vue
@@ -1,84 +1,94 @@
<template>
  <win-md class="stock-detail" :title="setting.title" @close="close" :width="'1100px'">
    <div v-loading="loading">
      <el-row :gutter="20">
        <el-col :span="6">
          <span>调拨单号:</span>
          <span>{{ detail.businessFormCode }}</span>
        </el-col>
        <el-col :span="6">
          <span>调拨机构:</span>
          <span>{{ detail.outAgencyName }}</span>
        </el-col>
        <el-col :span="6">
          <span>调拨人:</span>
          <span>{{ detail.outOperatorName || '-' }}</span>
        </el-col>
        <el-col :span="6">
          <span>接收机构:</span>
          <span>{{ detail.inAgencyName }}</span>
        </el-col>
      </el-row>
      <el-row :gutter="20" style="margin-top: 20px">
        <el-col :span="6">
          <span>申请人:</span>
          <span>{{ detail.operatorName }}</span>
        </el-col>
        <el-col :span="6">
          <span>申请调拨时间:</span>
          <span>{{ detail.createTime | formatTime }}</span>
        </el-col>
        <el-col :span="6">
          <span>状态:</span>
          <span>{{ getStatesLabel(detail.states) }}</span>
        </el-col>
        <el-col :span="6">
          <span>调拨时间:</span>
          <span>{{ detail.outputTime | formatTime }}</span>
        </el-col>
      </el-row>
      <el-row v-if="fileList && fileList.length" :gutter="20" style="margin-top: 20px">
        <el-col class="img-row" :span="24">
          <span>调拨手续照片:</span>
  <win-md class="stock-detail" :loading="loading" :title="setting.title" @close="close" :width="'1100px'">
    <el-row :gutter="20">
      <el-col :span="6">
        <span>调拨单号:</span>
        <span>{{ detail.businessFormCode }}</span>
      </el-col>
      <el-col :span="6">
        <span>调拨机构:</span>
        <span>{{ detail.outAgencyName }}</span>
      </el-col>
      <el-col :span="6">
        <span>调拨人:</span>
        <span>{{ detail.outOperatorName || '-' }}</span>
      </el-col>
      <el-col :span="6">
        <span>接收机构:</span>
        <span>{{ detail.inAgencyName }}</span>
      </el-col>
    </el-row>
    <el-row :gutter="20" style="margin-top: 20px">
      <el-col :span="6">
        <span>申请人:</span>
        <span>{{ detail.operatorName }}</span>
      </el-col>
      <el-col :span="6">
        <span>申请调拨时间:</span>
        <span>{{ detail.createTime | formatTime }}</span>
      </el-col>
      <el-col :span="6">
        <span>状态:</span>
        <span>{{ getStatesLabel(detail.states) }}</span>
      </el-col>
      <el-col :span="6">
        <span>调拨时间:</span>
        <span>{{ detail.outputTime | formatTime }}</span>
      </el-col>
    </el-row>
    <el-row v-if="fileList && fileList.length" :gutter="20" style="margin-top: 20px">
      <el-col class="img-row" :span="24">
        <span>调拨手续照片:</span>
        <div class="img-box" v-for="(item, index) in fileList" :key="index" @click="handlePreview(item)">
          <img class="img" :src="getUrl(item.path)" alt="" />
          <img class="img" :src="getUrl(item.path)" alt=""/>
        </div>
      </el-col>
    </el-row>
    <div class="goods-card" v-for="(goodsItem, goodsIndex) in detail.formTransferGoods" :key="goodsIndex">
      <el-row :gutter="20">
        <el-col :span="12">
          <span>物品分类:</span>
          <span>{{ goodsItem.categoryName }}</span>
        </el-col>
        <el-col :span="12">
          <span>物品名称:</span>
          <span>{{ goodsItem.goodsName }}</span>
        </el-col>
      </el-row>
      <div class="goods-card" v-for="(goodsItem, goodsIndex) in detail.formTransferGoods" :key="goodsIndex">
        <el-row :gutter="20">
          <el-col :span="12">
            <span>物品分类:</span>
            <span>{{ goodsItem.categoryName }}</span>
          </el-col>
          <el-col :span="12">
            <span>物品名称:</span>
            <span>{{ goodsItem.goodsName }}</span>
          </el-col>
        </el-row>
        <el-table :data="goodsItem.models" :stripe="true" style="margin-top: 20px">
          <el-table-column prop="baseGoodsModelsName" label="规格型号" align="center">
            <template slot-scope="scope">
              {{ scope.row.baseGoodsModelsName }}
            </template>
          </el-table-column>
          <el-table-column label="单位" align="center">
            <template slot-scope="scope">
              {{ scope.row.unit }}
            </template>
          </el-table-column>
          <el-table-column prop="counts" label="调拨数量" align="center">
            <template slot-scope="scope">
              {{ scope.row.counts }}
            </template>
          </el-table-column>
          <el-table-column prop="price" label="金额" align="center">
            <template slot-scope="scope">
              {{ scope.row.price }}
            </template>
          </el-table-column>
        </el-table>
      </div>
      <el-table :data="goodsItem.models" :stripe="true" style="margin-top: 20px">
        <el-table-column prop="baseGoodsModelsName" label="规格型号" align="center">
          <template slot-scope="scope">
            {{ scope.row.baseGoodsModelsName }}
          </template>
        </el-table-column>
        <el-table-column label="单位" align="center">
          <template slot-scope="scope">
            {{ scope.row.unit }}
          </template>
        </el-table-column>
        <el-table-column prop="counts" label="调拨数量" align="center">
          <template slot-scope="scope">
            {{ scope.row.counts }}
          </template>
        </el-table-column>
        <el-table-column prop="price" label="金额" align="center">
          <template slot-scope="scope">
            {{ scope.row.price }}
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div id="uploadPreviewImages" style="display: none">
      <span v-for="(src, index) in fileList" :key="index">
        <img
            v-if="checkImg(src.name)"
            class="v-img"
            :src="src.url"
            :alt="src.name"
            style="width: 100px; height: 100px"
        />
      </span>
    </div>
  </win-md>
</template>
@@ -87,21 +97,27 @@
import * as DateFormatter from '@/utils/DateFormatter';
import winMd from '@/components/win/win-md';
import transfer from '../../../mixins/transfer';
import {getDownUrl} from "@/utils/base";
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
let viewer = null;
export default {
  mixins: [transfer],
  components: { winMd },
  components: {winMd},
  props: {
    setting: {
      type: Object,
      default: () => {},
      default: () => {
      },
    },
  },
  data() {
    return {
      loading: false,
      detail: {},
      fileList:[]
      fileList: []
    };
  },
  filters: {
@@ -112,13 +128,60 @@
  },
  created() {
    this.loading = true;
    transferDetail({ id: this.setting.id }).then((res) => {
    transferDetail({id: this.setting.id}).then((res) => {
      this.detail = res;
      this.fileList = this.detail.procureDoc ? JSON.parse(this.detail.procureDoc) : [];
      this.fileList = this.detail.doc ? JSON.parse(this.detail.doc) : [];
      this.loading = false;
      this.$nextTick(() => {
        this.initPreviewImg();
      });
    });
  },
  methods: {
    initPreviewImg() {
      if (viewer != null) {
        viewer.destroy();
      }
      const ViewerDom = document.querySelector('#uploadPreviewImages');
      viewer = new Viewer(ViewerDom, {});
    },
    handlePreview(file) {
      if (!this.checkImg(file.name)) {
        return false;
      }
      let index = 0;
      for (let i = 0; i < this.fileList.length; i++) {
        const f = this.fileList[i];
        if (this.checkImg(f.name)) {
          if (file.id == f.id) {
            break;
          }
          index++;
        }
      }
      // this.fileList.forEach((f, i) => {
      //   if (file.uid == f.uid) {
      //     index = i
      //   }
      // })
      // document.querySelector('#uploadPreviewImages').children[0].click()
      viewer.view(index);
    },
    checkImg(name) {
      const suffix = name.substring(name.lastIndexOf('.'), name.length);
      const imgArray = ['.jpg', '.jpeg', '.png', '.bmp'];
      if (imgArray.indexOf(suffix) < 0) {
        return false;
      }
      return true;
    },
    getUrl(path) {
      if (path.substr(0, 7).toLowerCase() == 'http://' || path.substr(0, 8).toLowerCase() == 'https://') {
        return path;
      } else {
        return getDownUrl() + path;
      }
    },
    close() {
      this.$emit('close');
    },