123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- <template>
- <div class="product-store-container flex flex-col">
- <div class="product-store__header">
- <div class="search-box" v-if="type === '1'">
- <van-field v-model="searchVal" center placeholder="搜索" left-icon="search" @click="handleClickSearchBox">
- <template #button>
- <van-button size="small" @click="handleLaunchSearch" type="primary">搜索</van-button>
- </template>
- </van-field>
- </div>
- <div class="sub-title flex flex-row flex-row-aic flex-row-jcsp">
- <span v-if="type === '2'">请选择商品分类</span>
- <span v-else>商品库</span>
- <van-button v-show="searchData.length" size="small" @click="handleResetSearch" type="primary">重置搜索</van-button>
- </div>
- </div>
- <!-- 展示内容 -->
- <div class="product-store__main" v-if="searchData.length">
- <div class="search-row" v-for="(good, idx) in searchData" :key="idx" @click="handleSelectedGoods(good)">
- <div class="search-row__category">
- {{ good.goods_category_first_en }}/{{ good.goods_category_en }}
- </div>
- <div class="search-row__title ellipsis">{{ good.goods_name }}</div>
- </div>
- </div>
- <!-- 分类内容 -->
- <div class="product-store__main" v-else>
- <van-radio-group v-model="radioCategory">
- <div class="row" v-for="(item, idx) in categoryData" :key="idx">
- <div class="row__header row__header--b-line flex flex-row flex-row-aic">
- <div class="row__header__content">
- {{ item.name }}
- </div>
- <div class="row__header__more flex flex-row flex-row-aic" @click="handleClickRow(item, idx)">
- <van-icon name="cluster-o" :size="26" color="#3290C4" />
- <span class="txt">下级</span>
- </div>
- </div>
- <div class="row__main" v-if="item.expand">
- <div class="row__second" v-for="(second, idx2) in item.childlist" :key="idx2">
- <div class="row__second__header row__second__header--b-line flex flex-row flex-row-aic"
- @click="type === '1' ? handleClickSecondRow(second, idx2) : null">
- <!-- NOTE: Radio. 单选组件 -->
- <div class="radio-box" v-if="type === '2'">
- <van-radio :name="second.id">{{ second.name }}</van-radio>
- </div>
- <div v-else class="row__second__header__content">
- {{ second.name }}
- </div>
- <!-- NOTE: 只存在于选择商品 -->
- <div class="row__second__header__more" v-if="type === '1'">
- <van-icon v-if="second.expand" name="arrow-up" :size="24" color="rgba(162, 163, 164, 1)" />
- <van-icon v-else name="arrow-down" :size="24" color="rgba(162, 163, 164, 1)" />
- </div>
- </div>
- <div class="row__second__main" v-if="second.expand && second.childlist">
- <div class="row__third-item flex flex-row flex-row-aic" v-for="(good, idx3) in second.childlist"
- :key="idx3" @click="handleSelectedGoods(good, idx3)">
- <span id="c1">{{ good.goods_name }}</span>
- </div>
- </div>
- <div class="row__second__main" v-else>
- <div class="row__third-item flex flex-row flex-row-aic">
- <span id="c1">当前分类下暂无商品</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- </van-radio-group>
- </div>
- <!-- 提交 -->
- <div class="btn-container" v-if="type === '2'">
- <div class="btn-span" @click="handleConfirmCategory">确定</div>
- </div>
- <van-popup class="popup flex flex-col" v-model="popupVisibility" position="bottom" :style="{ height: '60%' }"
- closeable close-icon-position="top-right">
- <div class="popup__header">
- <span class="product__title" v-if="choosedStock">{{ choosedStock.goods_name }}</span>
- <span class="product__inventory">{{ leftStockComp }}</span>
- </div>
- <div class="popup__main">
- <template v-for="(item, idx) in productData">
- <template v-if="item.type === 'total'">
- <c-input :title="item.label" :placeholder="`请输入${item.label}`" :maxlength="5" :showWordLimit="false"
- input-type="digit" v-model="item.val" :key="idx" />
- </template>
- <template v-else-if="item.type === 'price'">
- <c-input :title="item.label" :placeholder="`请输入${item.label}`" :maxlength="5" :showWordLimit="false"
- input-type="number" v-model="item.val" :key="idx" />
- </template>
- <template v-else>
- <div :key="idx" class="product__row">
- <div class="product__row__header">
- {{ item.label }}
- </div>
- <div class="product__row__main">
- <span v-for="(type, idx) in item.list" :key="idx" :data-id="type.id"
- :class="{ 'selected': item.val == type.id }" @click="handleClickItem(item, type)">{{ type.name }}</span>
- </div>
- </div>
- </template>
- </template>
- </div>
- <div class="popup__footer">
- <div class="btn-container">
- <div class="btn-span" @click="handleConfirmInput">确认</div>
- </div>
- </div>
- </van-popup>
- </div>
- </template>
- <style lang="less" scoped>
- @import url('@/styles/variables.less');
- .product-store {
- &-container {
- height: 100vh;
- justify-content: space-between;
- }
- &__header {
- padding: 10px 12px 0;
- background-color: @white;
- .search-box {
- .van-cell.van-field {
- background: rgba(118, 118, 128, 0.12);
- border-radius: 8px;
- }
- }
- .sub-title {
- font-size: @font-size-third;
- font-weight: 400;
- color: #727273;
- line-height: 18px;
- padding: 10px 0;
- }
- .van-button--primary {
- background-color: @main-color;
- border: 1px solid @main-color;
- }
- }
- &__main {
- padding: 10px 0;
- height: 0;
- flex: 1;
- .row {
- margin-bottom: 10px;
- background-color: @white;
- &__header {
- padding: 10px 12px;
- justify-content: space-between;
- &--b-line {
- border-bottom: 1px solid #eee;
- }
- &__content {
- font-size: @font-size-secondery;
- }
- &__more {
- font-size: 16px;
- border-left: 1px solid rgba(151, 151, 151, 0.4);
- padding: 3px 10px 3px 20px;
- span.txt {
- padding-left: 6px;
- color: #3290c4;
- }
- }
- }
- &__main {
- padding-left: 36px;
- }
- &__second {
- &__header {
- padding: 10px 12px;
- justify-content: space-between;
- &--b-line {
- border-bottom: 1px solid #eee;
- }
- &__content {
- font-size: @font-size-secondery;
- }
- &__more {}
- /deep/.van-radio__label {
- font-size: 14px;
- }
- // .van-radio__icon--checked .van-icon
- /deep/.van-radio__icon--checked .van-icon {
- background-color: @main-color;
- border-color: @main-color;
- }
- }
- &__main {
- padding-left: 30px;
- }
- }
- &__third-item {
- padding: 10px 12px;
- font-size: @font-size-third;
- border-bottom: 1px solid #eee;
- &:last-child {
- border-bottom-color: transparent;
- }
- }
- }
- .search-row {
- padding: 10px 10px;
- background-color: #fff;
- &__category {
- font-size: 12px;
- color: #8d8c8c;
- margin-bottom: 8px;
- }
- &__title {
- font-size: 16px;
- }
- }
- }
- }
- .popup {
- justify-content: space-between;
- padding: 8px 0 0;
- box-sizing: border-box;
- &__header {
- margin: 0 12px;
- padding-right: 30px;
- padding-bottom: 10px;
- padding-top: 8px;
- border-bottom: 1px solid rgba(151, 151, 151, 0.3);
- font-size: @font-size-common;
- }
- &__main {
- margin: 18px 0;
- height: 0;
- flex: 1;
- overflow: auto;
- .layout-container {
- padding-top: 12px;
- }
- }
- &__footer {
- position: relative;
- z-index: 9;
- border-top: 12px solid rgba(248, 248, 248, 1);
- box-shadow: 0 -2px 16px 1px rgba(0, 0, 0, 0.2);
- .btn-container {
- margin-top: initial;
- }
- }
- }
- .product {
- &__row {
- padding: 6px 12px 16px;
- &:nth-last-of-type(2) {
- border-bottom: 1px solid rgba(151, 151, 151, 0.3);
- }
- }
- &__title {
- font-size: 16px;
- font-weight: 500;
- color: #191A1E;
- }
- &__inventory {
- padding-left: 10px;
- font-size: 12px;
- font-weight: 400;
- color: #727273;
- vertical-align: bottom;
- }
- &__row {
- &__header {
- font-size: @font-size-common;
- padding: 8px 0 10px;
- color: #191A1E;
- }
- &__main {
- display: grid;
- grid-template-columns: repeat(2, 49%);
- column-gap: 2%;
- row-gap: 10px;
- span {
- display: inline-block;
- text-align: center;
- background: #EFF7FB;
- border-radius: 8px;
- font-size: @font-size-secondery;
- font-weight: 400;
- color: #727273;
- padding: 8px 0;
- transition-property: background-color, color;
- transition-duration: 0.2s;
- &.selected {
- background: #0a83d3;
- color: @white;
- }
- }
- }
- }
- }
- </style>
- <script>
- /**
- * 商品库页面
- * @description 当前页面处理为多种展示格式。
- * - 商品单选
- * - 分类选择
- */
- import vueBus from '@/utils/vueBus';
- import CInput from './components/CInput.vue';
- import * as goodsApi from '@/api/goods'
- export default {
- name: "ProductStore",
- components: {
- CInput
- },
- computed: {
- leftStockComp() {
- if (!this.choosedStock) return ''
- const [{ val, list }] = this.productData
- let currentData = list.filter(item => item.id === val)
- if (currentData.length) {
- let len = currentData[0].stock
- return len > 0 ? `库存剩余:${len}件` : `暂无库存`
- } else return ''
- }
- },
- data: () => ({
- type: "1", // 页面状态。 默认1:商品选择。 可选项2:选择分类
- module: '', // 进入module不同时有些许差别
- radioCategory: '', // 二级分类Id
- searchVal: '', // 搜索商品?
- categoryData: [], // 分类数据
- // NOTE: Popup data and context data
- popupVisibility: false,
- productData: [], // 弹出选择商品的数量等。
- choosedStock: null,
- searchData: [], // 搜索数据存储列表
- }),
- created() {
- this.__init__()
- },
- methods: {
- async __init__() {
- try {
- let query = this.$route.query
- if (query) {
- // type = 1 // 页面类型。1 => 选择商品。 2 => 选择分类
- if (query.type) this.type = query.type
- // 申请的`module`
- if (query.module) {
- this.module = query.module
- }
- }
- await this.__queryCategoay__() // initalization query categray
- } catch (error) {
- console.log('ProductStore __init__ error', error);
- }
- },
- handleClickSearchBox() { },
- // 点击一级
- handleClickRow(row) {
- row.expand = !row.expand
- this.$forceUpdate()
- },
- // 点击二级
- async handleClickSecondRow(row) {
- const toastInstance = this.$toast({
- type: 'loading',
- message: '加载数据中',
- duration: 0
- })
- try {
- if (!row.expand && !row.childlist) { // NOTE: 只有打开时&无子级别查询
- const list = await this.__queryStoreData__(row.id)
- row.childlist = [...list]
- }
- row.expand = !row.expand
- this.$forceUpdate()
- } catch (error) {
- this.$toast(error.message)
- } finally {
- toastInstance.clear()
- }
- },
- // 选择类型
- handleClickItem(row, item) {
- row.val = item.id
- },
- // NOTE: popup confirm 确认数据正常
- handleConfirmInput() {
- try {
- let _list = this.productData
- let isAllInput = _list.every(item => item.val)
- if (!isAllInput) return this.$toast('检查填写情况')
- const [choosedGoodsId, customCount, GoodsPrice] = _list.map(item => item.val)
- // 选中的类别对象
- let choosedGoodsS = _list[0].list.filter(item => item.id === choosedGoodsId)[0]
- let ChoosedGoodsStock = 0
- if (choosedGoodsS) {
- ChoosedGoodsStock = choosedGoodsS.stock
- }
- // 判断输入的数量是否大于库存
- if (customCount <= 0) {
- return this.$toast('物品数量最少1件')
- }
- if (this.module != 3 && customCount > ChoosedGoodsStock) {
- return this.$toast('当前商品规格数量不足')
- }
- if (GoodsPrice <= 0) {
- return this.$toast('当前商品价格不对')
- }
- // if (this.type === '1') {}
- vueBus.$emit('updateProductList', {
- item: this.choosedStock,
- goodsStock: choosedGoodsS,
- customCount,
- GoodsPrice
- })
- this.$router.go(-1);
- } catch (error) {
- console.log(error);
- }
- },
- // NOTE: 选中商品进行业务
- handleSelectedGoods(item) {
- const { goods_stock } = item
- this.choosedStock = item
- let temporaryData = [
- {
- label: '规格',
- val: '',
- list: goods_stock
- },
- {
- label: '物品数量',
- type: 'total',
- val: ''
- }
- ]
- // 申购明细时需要物品单价
- if (['1'].includes(this.module)) {
- temporaryData.push({
- label: '物品单价',
- type: 'price',
- val: ''
- })
- }
- this.productData = temporaryData
- this.popupVisibility = true
- },
- // NOTE: 查询分类数据
- __queryCategoay__() {
- return new Promise((resolve, reject) => {
- goodsApi.category().then(result => {
- if (result.code === 1) {
- this.categoryData = result.data
- resolve()
- } else {
- reject()
- }
- }).catch(error => {
- console.log('%c error >>>', 'background: blue; color: #fff', error);
- reject()
- })
- })
- },
- // NOTE: 查询商品数据
- // @returns [] | throw error
- __queryStoreData__(category_id) {
- return new Promise((resolve, reject) => {
- goodsApi.list({
- category_id
- }).then(result => {
- if (result.code === 1) {
- resolve(result.data || [])
- }
- }).catch(error => {
- reject(error)
- })
- })
- },
- // NOTE: 确定选择的分类
- handleConfirmCategory() {
- let selId = this.radioCategory
- let arrs = this.categoryData
- for (let i = 0; i < arrs.length; i++) {
- const row = arrs[i];
- const childlist = row.childlist
- for (let j = 0; j < childlist.length; j++) {
- const item = childlist[j];
- if (selId === item.id) {
- let first = { ...row }
- delete first.childlist
- delete first.expand
- vueBus.$emit('listenCategoryEvent', {
- first,
- second: { ...item }
- })
- break
- } else continue
- }
- }
- this.$router.go(-1)
- },
- // NOTE: 根据id展示分类名称
- __category_en__(good) {
- const { goods_category_first, goods_category_id } = good
- const temporary = {
- goods_category_first_en: '',
- goods_category_en: ''
- }
- let arrs = this.categoryData
- for (let i = 0; i < arrs.length; i++) {
- const firs = arrs[i];
- if (firs.id === goods_category_first) {
- for (let j = 0; j < firs.childlist.length; j++) {
- const itm = firs.childlist[j];
- if (itm.id === goods_category_id) {
- temporary.goods_category_first_en = firs.name
- temporary.goods_category_en = itm.name
- break
- } else continue
- }
- } else continue
- }
- return temporary
- },
- // NOTE: Search Event
- async handleLaunchSearch() {
- let keyword = this.searchVal
- if (!keyword) return
- const toastInstance = this.$toast({
- type: 'loading',
- message: '搜索中...',
- duration: 0
- })
- try {
- const result = await goodsApi.list({
- search: keyword
- })
- if (result.code === 1) {
- if (result.data.length) {
- this.searchData = result.data.map(good => ({
- ...good,
- ...this.__category_en__(good)
- }))
- toastInstance.clear()
- } else {
- toastInstance.clear()
- this.$toast(`暂无与${keyword}相关商品`)
- }
- }
- } catch (error) {
- toastInstance.clear()
- console.log('%c handleLaunchSearchError >>>', 'background: blue; color: #fff', error);
- }
- },
- // Reset search
- handleResetSearch() {
- this.searchData = []
- this.searchVal = ''
- },
- },
- watch: {
- radio(val) {
- console.log('%c radio value watch? >>>', 'background: blue; color: #fff', val);
- }
- }
- }
- </script>
|