This commit is contained in:
郑彪辉
2025-04-16 21:34:05 +08:00
commit ada40c1638
1187 changed files with 141883 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh] overflow-hidden form-edit" :style="themeColor()">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef"
labelWidth="180rpx">
<view class="sidebar-margin card-template mt-[var(--top-m)] py-[20rpx]">
<view>
<u-form-item :label="t('productTypeId')" prop="name" required borderBottom>
<view class="flex w-full items-center h-[52rpx]" @click="pidSheetShow = true">
<view v-if="!formData.product_type_id" class="text-[#888] text-[28rpx] flex-1">
{{ t('productTypeIdPlaceholder') }}
</view>
<view v-else class="text-[28rpx] flex-1 leading-[1.4]">{{ formData.product_type_name }}
</view>
</view>
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('name')" prop="name" required borderBottom>
<u-input fontSize="28rpx" v-model.trim="formData.name" border="none" clearable
placeholderStyle="color: #888" :placeholder="t('namePlaceholder')" />
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('productUnitId')" required prop="product_unit_id" borderBottom>
<view class="flex w-full items-center h-[52rpx]" @click="unitSheetShow = true">
<view v-if="!formData.product_unit_id" class="text-[#888] text-[28rpx] flex-1">
{{ t('productUnitIdPlaceholder') }}
</view>
<view v-else class="text-[28rpx] flex-1 leading-[1.4]">{{ formData.product_unit_name }}
</view>
</view>
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('spec')" prop="spec" borderBottom>
<u-input fontSize="28rpx" v-model.trim="formData.spec" border="none" clearable
placeholderStyle="color: #888" :placeholder="t('specPlaceholder')" />
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('minWarning')" prop="min_warning" borderBottom>
<u-input fontSize="28rpx" type="number" v-model="formData.min_warning"
:placeholder="t('minWarningPlaceholder')" border="none"></u-input>
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('maxWarning')" prop="max_warning" borderBottom>
<u-input fontSize="28rpx" type="number" v-model="formData.max_warning" border="none" clearable
maxlength="120" :placeholder="t('maxWarningPlaceholder')" placeholderStyle="color: #888" />
</u-form-item>
</view>
<view class="mt-[16rpx]">
<u-form-item :label="t('remark')" prop="remark">
<u-textarea fontSize="28rpx" v-model="formData.remark" :placeholder="t('remarkPlaceholder')"
border="none"></u-textarea>
</u-form-item>
</view>
</view>
</u-form>
<!-- 产品分类 -->
<u-action-sheet class="" :actions="pidList" :show="pidSheetShow" :closeOnClickOverlay="true"
:safeAreaInsetBottom="true" @close="pidSheetShow = false" @select="updateSex"></u-action-sheet>
<!-- 产品单位 -->
<u-action-sheet class="" :actions="unitList" :show="unitSheetShow" :closeOnClickOverlay="true"
:safeAreaInsetBottom="true" @close="unitSheetShow = false" @select="updateSexUnit"></u-action-sheet>
<view class="w-full footer">
<view
class="py-[var(--top-m)] px-[var(--sidebar-m)] footer w-full fixed bottom-0 left-0 right-0 box-border">
<button hover-class="none"
class="!bg-[var(--primary-color)] text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500"
@click="handSave" :disabled="loading" :class="{'opacity-50': loading}">{{t('save')}}</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { t } from '@/locale'
import { onLoad } from '@dcloudio/uni-app';
import { getProductTypeSelect, getProductUnitSelect, addProduct } from '@/addon/erp/api/product';
import { generatedCode } from '@/addon/erp/utils/common';
import { redirect } from '@/utils/common';
const formRef : any = ref(null)
const loading = ref(false)
const areaRef = ref()
const isSelectAddress = ref(false)
const rules = computed(() => {
return {
'product_type_id': {
type: 'string',
required: true,
message: t('productTypeIdPlaceholder'),
trigger: ['blur', 'change'],
},
'name': {
type: 'string',
required: true,
message: t('namePlaceholder'),
trigger: ['blur', 'change'],
},
'product_unit_id': {
type: 'string',
required: true,
message: t('productUnitIdPlaceholder'),
trigger: ['blur', 'change'],
},
}
})
// 页面加载
onLoad((option : any) => {
getCateList();
getUnitList();
})
const formData = ref({
id: "",
code: generatedCode("P"),
name: "",
product_type_id: "",
product_type_name: "",
spec: "",
inventory: "",
min_warning: "",
max_warning: "",
remark: "",
product_unit_id: "",
product_unit_name: "",
image: "",
})
/**
* 分类
*/
const pidSheetShow = ref(false)
const pidList = ref<any[]>([]);
const updateSex = (e : any) => {
formData.value.product_type_id = e.id;
formData.value.product_type_name = e.name;
}
// 获取分类
const getCateList = () => {
getProductTypeSelect().then((res : any) => {
pidList.value = res.data;
}).catch(err => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
})
}
/**
* 单位
*/
const unitSheetShow = ref(false)
const unitList = ref<any[]>([]);
const updateSexUnit = (e : any) => {
formData.value.product_unit_id = e.id;
formData.value.product_unit_name = e.name;
}
// 获取单位
const getUnitList = () => {
getProductUnitSelect({}).then((res : any) => {
unitList.value = res.data;
}).catch(err => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
})
}
// 保存
const handSave = () => {
formRef.value.validate().then(() => {
if (loading.value) return
loading.value = true
addProduct(formData.value).then((res : any) => {
loading.value = false
if (res.code == 1) {
redirect({ url: '/addon/erp/pages/product/product/list' })
}
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
loading.value = false
})
})
}
</script>
<style lang="scss" scoped>
.form-edit :deep(.u-form-item__body__left__content__label) {
font-size: 28rpx !important;
}
.form-edit :deep(.u-form-item__body__right) {
display: flex;
align-items: center;
}
.footer {
height: calc(100rpx + var(--top-m) + var(--top-m) + constant(safe-area-inset-bottom)) !important;
height: calc(100rpx + var(--top-m) + var(--top-m) + env(safe-area-inset-bottom)) !important;
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<view class="bg-[#ffffff] min-h-[100vh]" :style="themeColor()">
<view class="mx-[30rpx]">
<!-- 注意如果需要兼容微信小程序最好通过setRules方法设置rules规则 -->
<u-form labelPosition="left" :model="formData" labelWidth="190rpx" ref="formRef">
<u-form-item :label="t('code')" borderBottom>
{{formData.code}}
</u-form-item>
<u-form-item :label="t('name')" borderBottom>
{{formData.name}}
</u-form-item>
<u-form-item :label="t('productTypeId')" borderBottom>
{{formData.productType?.name}}
</u-form-item>
<u-form-item :label="t('spec')" borderBottom>
{{formData.spec}}
</u-form-item>
<u-form-item :label="t('productUnitId')" borderBottom>
{{formData.productUnit?.name}}
</u-form-item>
<u-form-item :label="t('minWarning')" borderBottom>
{{formData.min_warning}}
</u-form-item>
<u-form-item :label="t('maxWarning')" borderBottom>
{{formData.max_warning}}
</u-form-item>
<u-form-item :label="t('remark')" borderBottom>
{{formData.remark}}
</u-form-item>
<u-form-item :label="t('createTime')" borderBottom>
{{formData.create_time}}
</u-form-item>
</u-form>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { t } from '@/locale'
import { onLoad } from '@dcloudio/uni-app';
import { getProductInfo } from '@/addon/erp/api/product';
const formRef : any = ref(null)
const formData = reactive({
code: "",
name: '',
productType: {
name: ''
},
spec: '',
productUnit: {
name: ''
},
inventory: '',
min_warning: '',
max_warning: '',
remark: '',
create_time: '',
})
// 加载
onLoad((data : any) => {
getInfo(data.id);
})
const getInfo = async (id : number) => {
const data = await (await getProductInfo(id)).data;
Object.assign(formData, data);
}
</script>
<style>
</style>

View File

@@ -0,0 +1,160 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh]" :style="themeColor()">
<mescroll-body ref="mescrollRef" @init="mescrollInit" :down="{ use: false }" height="auto" @up="getListFn"
:top="mescrollTop">
<view class="sidebar-margin body-bottom" v-if="productList.length">
<view class="mb-[15rpx]" v-for="(item,index) in productList" :key="item.id">
<view class="bg-[#ffffff] rounded-10rpx">
<view class="head border-0 border-b-1 border-solid border-[#ddd]">
<view class="flex justify-between p-[20rpx]">
<view class="u-body-item-title u-line-2">{{item.name}}</view>
</view>
</view>
<view class="body">
<view class="flex justify-start flex-col p-[20rpx]">
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('code') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.code}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('productTypeId') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.productType?.name}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('spec') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.spec}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('inventory') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.inventory}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('productUnitId') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.productUnit?.name}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('createTime') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.create_time}}</text>
</view>
</view>
</view>
<view class="foot border-0 border-t-1 border-solid border-[#ddd]">
<view class="flex justify-end p-[20rpx]">
<view><up-button :text="t('info')" size="mini"
@click="redirect({ url: '/addon/erp/pages/product/product/detail',param:{id: item.id} })"></up-button>
</view>
<view><up-button type="error" @click="hanldeDelete(item.id)" :text="t('delete')"
size="mini"></up-button></view>
</view>
</view>
</view>
<!-- 删除模态框 -->
<up-modal :show="show" @confirm="confirm" showCancelButton @cancel="show = false"
:title="t('delete')" :content="t('productDeleteTips')" ref="uModal"></up-modal>
</view>
</view>
<mescroll-empty v-if="!productList.length && !loading &&!listLoading"></mescroll-empty>
</mescroll-body>
<loading-page :loading="loading"></loading-page>
<view class="fixed bottom-4 right-4 z-2">
<up-button @click="redirect({ url: '/addon/erp/pages/product/product/add'})" type="primary" shape="circle"
:customStyle="{borderRadius: '50%',padding: 0,width: '50px',height: '50px'}">
<u-icon size="30" color="#fff" name="plus-circle-fill"></u-icon>
</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue'
import { topTabar } from '@/utils/topTabbar';
import { t } from '@/locale';
import { redirect, pxToRpx } from '@/utils/common';
import MescrollBody from '@/components/mescroll/mescroll-body/mescroll-body.vue';
import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
import { getProductList, deleteProduct } from '@/addon/erp/api/product';
import { onShow, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
const { downCallback, mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
const show = ref(false);
// 获取系统状态栏的高度
let menuButtonInfo : any = {};
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API尚未兼容)
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
// #endif
const mescrollTop = "20rpx"
const productList = ref<Array<any>>([]),
loading = ref<boolean>(true),
listLoading = ref<boolean>(true),
mescrollRef = ref(null);
interface mescrollStructure {
num : number,
size : number,
endSuccess : Function,
[propName : string] : any
}
const getListFn = (mescroll : mescrollStructure) => {
listLoading.value = true;
let data : Object = {
page: mescroll.num,
limit: mescroll.size
};
getProductList(data).then((res : any) => {
let newArr = res.data.data;
mescroll.endSuccess(newArr.length);
//设置列表数据
if (mescroll.num == 1) {
productList.value = []; //如果是第一页需手动制空列表
}
productList.value = productList.value.concat(newArr);
listLoading.value = false;
loading.value = false;
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
listLoading.value = false;
loading.value = false;
mescroll.endErr(); // 请求失败, 结束加载
})
}
// 删除
const del_id = ref<number>(0);
const hanldeDelete = (id : number) => {
show.value = true;
del_id.value = id;
};
// 确认删除
const confirm = () => {
if (del_id.value == 0) {
uni.showToast({ icon: 'none', title: t("Invalid ID") });
return false;
}
let index = productList.value.findIndex(item => item.id === del_id.value);
deleteProduct(del_id.value)
.then(() => {
productList.value.splice(index, 1);
del_id.value = 0;
show.value = false;
}).catch(() => { });
}
</script>
<style>
</style>

View File

@@ -0,0 +1,145 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh]" :style="themeColor()">
<mescroll-body ref="mescrollRef" @init="mescrollInit" :down="{ use: false }" height="auto" @up="getListFn"
:top="mescrollTop">
<view class="sidebar-margin body-bottom" v-if="productList.length">
<u-checkbox-group class="w-full" v-model="checkProductList" placement="column">
<view class="mb-[15rpx]" v-for="(item,index) in productList" :key="index">
<view class="bg-[#ffffff] rounded-10rpx">
<view class="head border-0 border-b-1 border-solid border-[#ddd]">
<view class="flex justify-between p-[20rpx]">
<view class="u-body-item-title u-line-2">{{item.name}}</view>
</view>
</view>
<view class="body">
<view class="flex justify-start flex-col p-[20rpx]">
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('code') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.code}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('productTypeId') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.productType?.name}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('spec') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.spec}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('inventory') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.inventory}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('productUnitId') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.productUnit?.name}}</text>
</view>
<view class="flex justify-start mb-[18rpx] text-[12px] text-[#6a6a6a]">
{{ t('createTime') }}:<text
class="ml-[20rpx] text-[var(--primary-color)]">{{item.create_time}}</text>
</view>
</view>
</view>
<view class="foot border-0 border-t-1 border-solid border-[#ddd]">
<view class="flex justify-start p-[20rpx]">
<u-checkbox :name="item" :label="t('choose')"></u-checkbox>
</view>
</view>
</view>
</view>
</u-checkbox-group>
</view>
<mescroll-empty v-if="!productList.length && !loading &&!listLoading"></mescroll-empty>
</mescroll-body>
<loading-page :loading="loading"></loading-page>
<view class="sidebar-margin mt-[20rpx] body-bottom">
<u-button @click="onConfirm" type="primary" shape="circle" :text="t('confirmChoose')"></u-button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue'
import { topTabar } from '@/utils/topTabbar';
import { t } from '@/locale';
import { redirect, pxToRpx } from '@/utils/common';
import MescrollBody from '@/components/mescroll/mescroll-body/mescroll-body.vue';
import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
import { getProductList, getProductListByWarehouse } from '@/addon/erp/api/product';
import { onLoad, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
const { downCallback, mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
const checkProductList = ref<any[]>([]);
const queryType = ref<any>('all'); // 查询方式 all 获取全部产品, warehouse 根据仓库ID ,获取对应的产品
const warehouse_id = ref<any>(undefined); // 仓库Id
// 页面加载
onLoad((option : any) => {
queryType.value = option.type ? option.type : '';
warehouse_id.value = option.warehouse_id ? option.warehouse_id : undefined
})
const mescrollTop = "20rpx"
const productList = ref<Array<any>>([]),
loading = ref<boolean>(true),
listLoading = ref<boolean>(true),
mescrollRef = ref(null);
interface mescrollStructure {
num : number,
size : number,
endSuccess : Function,
[propName : string] : any
}
const getListFn = (mescroll : mescrollStructure) => {
listLoading.value = true;
let data : Object = {
page: mescroll.num,
limit: mescroll.size,
warehouse_id: warehouse_id.value
};
let getDataApi =
queryType.value == "all" ? getProductList : getProductListByWarehouse;
getDataApi(data).then((res : any) => {
let newArr = res.data.data;
mescroll.endSuccess(newArr.length);
//设置列表数据
if (mescroll.num == 1) {
productList.value = []; //如果是第一页需手动制空列表
}
productList.value = productList.value.concat(newArr);
listLoading.value = false;
loading.value = false;
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
listLoading.value = false;
loading.value = false;
mescroll.endErr(); // 请求失败, 结束加载
})
}
// 确认选择
const onConfirm = () => {
uni.$emit('choose_product', checkProductList.value);
// 返回上一页
uni.navigateBack({
delta: 1
});
}
</script>
<style>
</style>

View File

@@ -0,0 +1,126 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh] overflow-hidden form-edit" :style="themeColor()">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef"
labelWidth="160rpx">
<view class="sidebar-margin card-template mt-[var(--top-m)] py-[20rpx]">
<u-form-item :label="t('pid')" prop="pid" borderBottom>
<view class="flex w-full items-center h-[52rpx]" @click="pidSheetShow = true">
<view v-if="!formData.pid" class="text-[#888] text-[28rpx] flex-1">
{{ t('pidPlaceholder') }}</view>
<view v-else class="text-[28rpx] flex-1 leading-[1.4]">{{ formData.pid_name }}</view>
</view>
</u-form-item>
<u-form-item :label="t('name')" prop="name" borderBottom required>
<u-input v-model="formData.name" :placeholder="t('namePlaceholder')" border="none"></u-input>
</u-form-item>
</view>
</u-form>
<view class="w-full footer">
<view class="py-[var(--top-m)] px-[var(--sidebar-m)] footer w-full fixed bottom-0 left-0 right-0 box-border">
<button hover-class="none" class="!bg-[var(--primary-color)] text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500"
@click="handSave" :disabled="loading" :class="{'opacity-50': loading}">{{t('save')}}</button>
</view>
</view>
<!-- 上级分类 -->
<u-action-sheet class="" :actions="pidList" :show="pidSheetShow" :closeOnClickOverlay="true"
:safeAreaInsetBottom="true"
@close="pidSheetShow = false" @select="updateSex"></u-action-sheet>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { t } from '@/locale'
import { onLoad } from '@dcloudio/uni-app';
import { getProductTypeSelect, getProductTypeInfo, addProductType, editProductType } from '@/addon/erp/api/product';
import { redirect } from '@/utils/common';
const formRef: any = ref(null)
const loading = ref(false)
// 页面加载
onLoad((option: any) => {
getCateList();
if(option.id){
getInfo(option.id);
}
})
const rules = reactive({
name: {
type: 'string',
required: true,
message: t('namePlaceholder'),
trigger: ['blur', 'change'],
}
});
const formData = ref({
id: 0,
pid: 0,
pid_name: '',
name: '',
sort: 20
})
/**
* 上级分类
*/
const pidSheetShow = ref(false)
const pidList = ref<any[]>([]);
const updateSex = (e: any) => {
formData.value.pid = e.id;
formData.value.pid_name = e.name;
}
// 获取上级分类
const getCateList = () => {
getProductTypeSelect().then((res:any) => {
pidList.value = res.data;
}).catch(err => {
// 如果是4001没有绑定企业账号强制跳转绑定
if(err.code == 4001){
redirect({ url: '/addon/erp/pages/member/bind' })
}
})
}
const getInfo = (id: number) => {
getProductTypeInfo(id).then((res: any) => {
formData.value = res.data;
}).catch(err => {
// 如果是4001没有绑定企业账号强制跳转绑定
if(err.code == 4001){
redirect({ url: '/addon/erp/pages/member/bind' })
}
})
}
// 保存
const handSave = () => {
formRef.value.validate().then(() => {
if (loading.value) return
loading.value = true
const save = formData.value.id ? editProductType : addProductType;
save(formData.value).then((res: any) => {
if(res.code == 1){
redirect({ url: '/addon/erp/pages/product/type/list' })
}
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if(err.code == 4001){
redirect({ url: '/addon/erp/pages/member/bind' })
}
loading.value = false
})
})
}
</script>
<style>
</style>

View File

@@ -0,0 +1,152 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh]" :style="themeColor()">
<mescroll-body ref="mescrollRef" @init="mescrollInit" :down="{ use: false }" height="auto" @up="getListFn"
:top="mescrollTop">
<view class="sidebar-margin body-bottom" v-if="productTypeList.length">
<u-swipe-action class="bg-[#ffffff]" v-for="(item,index) in productTypeList" :key="index">
<u-swipe-action-item :name="item.id" :ref="setSwipeActionRef(index)"
@click="(v) => hanldeOptionCallBack(v, item.id)" :options="btnOptions">
<view class="border-0 border-b-1 border-solid border-[#ccc]">
<view class="p-[20rpx] leading-normal">
<text class="text-[12px] text-[#6a6a6a]">{{item.name}}</text>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
<up-modal :show="show" @confirm="confirm" showCancelButton @cancel="show = false" :title="t('delete')"
:content="t('productTypeDeleteTips')" ref="uModal"></up-modal>
</view>
<mescroll-empty v-if="!productTypeList.length && !loading &&!listLoading"></mescroll-empty>
</mescroll-body>
<loading-page :loading="loading"></loading-page>
<view class="fixed bottom-4 right-4 z-2">
<up-button @click="redirect({ url: '/addon/erp/pages/product/type/add'})" type="primary" shape="circle"
:customStyle="{borderRadius: '50%',padding: 0,width: '50px',height: '50px'}">
<u-icon size="30" color="#fff" name="plus-circle-fill"></u-icon>
</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { t } from '@/locale';
import { redirect, pxToRpx } from '@/utils/common';
import MescrollBody from '@/components/mescroll/mescroll-body/mescroll-body.vue';
import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
import { getProductTypeList, deleteProductType } from '@/addon/erp/api/product';
import { onShow, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
const { downCallback, mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
const show = ref(false);
// 获取系统状态栏的高度
let menuButtonInfo : any = {};
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API尚未兼容)
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
// #endif
const mescrollTop = "20rpx"
const productTypeList = ref<Array<any>>([]),
loading = ref<boolean>(true),
listLoading = ref<boolean>(true),
mescrollRef = ref(null);
interface mescrollStructure {
num : number,
size : number,
endSuccess : Function,
[propName : string] : any
}
const getListFn = (mescroll : mescrollStructure) => {
listLoading.value = true;
let data : Object = {
page: mescroll.num,
limit: mescroll.size
};
getProductTypeList(data).then((res : any) => {
let newArr = res.data.data;
mescroll.endSuccess(newArr.length);
//设置列表数据
if (mescroll.num == 1) {
productTypeList.value = []; //如果是第一页需手动制空列表
}
productTypeList.value = productTypeList.value.concat(newArr);
listLoading.value = false;
loading.value = false;
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
listLoading.value = false;
loading.value = false;
mescroll.endErr(); // 请求失败, 结束加载
})
}
const btnOptions = ref([{
text: t('edit'),
style: {
backgroundColor: '#3c9cff'
}
}, {
text: t('delete'),
style: {
backgroundColor: '#f56c6c'
}
}])
const swipeActionRefs = ref<any[]>([]);
const setSwipeActionRef = (index : number) => (el : any[] | null) => {
if (el) {
swipeActionRefs.value[index] = el;
}
};
// 按钮回调
const del_id = ref<number>(0);
const hanldeOptionCallBack = (obj : any, id : number) => {
// 第一个编辑按钮
if (obj.index === 0) {
redirect({ url: '/addon/erp/pages/product/type/add', param: { id: id } })
}
// 第二个删除按钮
if (obj.index === 1) {
show.value = true;
del_id.value = id;
}
};
// 确认删除
const confirm = () => {
if (del_id.value == 0) {
uni.showToast({ icon: 'none', title: t("Invalid ID") });
return false;
}
let index = productTypeList.value.findIndex(item => item.id === del_id.value);
deleteProductType(del_id.value)
.then(() => {
productTypeList.value.splice(index, 1);
del_id.value = 0;
show.value = false;
if (swipeActionRefs.value[index]) {
swipeActionRefs.value[index].closeHandler();
}
}).catch(() => { });
}
</script>
<style>
</style>

View File

@@ -0,0 +1,90 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh] overflow-hidden form-edit" :style="themeColor()">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef"
labelWidth="160rpx">
<view class="sidebar-margin card-template mt-[var(--top-m)] py-[20rpx]">
<u-form-item :label="t('name')" prop="name" borderBottom required>
<u-input v-model="formData.name" :placeholder="t('namePlaceholder')" border="none"></u-input>
</u-form-item>
</view>
</u-form>
<view class="w-full footer">
<view class="py-[var(--top-m)] px-[var(--sidebar-m)] footer w-full fixed bottom-0 left-0 right-0 box-border">
<button hover-class="none" class="!bg-[var(--primary-color)] text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500"
@click="handSave" :disabled="loading" :class="{'opacity-50': loading}">{{t('save')}}</button>
</view>
</view>
<!-- 上级分类 -->
<u-action-sheet class="" :actions="pidList" :show="pidSheetShow" :closeOnClickOverlay="true"
:safeAreaInsetBottom="true"
@close="pidSheetShow = false" @select="updateSex"></u-action-sheet>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { t } from '@/locale'
import { onLoad } from '@dcloudio/uni-app';
import { getProductUnitInfo, addProductUnit, editProductUnit } from '@/addon/erp/api/product';
import { redirect } from '@/utils/common';
const formRef: any = ref(null)
const loading = ref(false)
// 页面加载
onLoad((option: any) => {
if(option.id){
getInfo(option.id);
}
})
const rules = reactive({
name: {
type: 'string',
required: true,
message: t('namePlaceholder'),
trigger: ['blur', 'change'],
}
});
const formData = ref({
id: 0,
name: '',
})
const getInfo = (id: number) => {
getProductUnitInfo(id).then((res: any) => {
formData.value = res.data;
}).catch(err => {
// 如果是4001没有绑定企业账号强制跳转绑定
if(err.code == 4001){
redirect({ url: '/addon/erp/pages/member/bind' })
}
})
}
// 保存
const handSave = () => {
formRef.value.validate().then(() => {
if (loading.value) return
loading.value = true
const save = formData.value.id ? editProductUnit : addProductUnit;
save(formData.value).then((res: any) => {
if(res.code == 1){
redirect({ url: '/addon/erp/pages/product/unit/list' })
}
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if(err.code == 4001){
redirect({ url: '/addon/erp/pages/member/bind' })
}
loading.value = false
})
})
}
</script>
<style>
</style>

View File

@@ -0,0 +1,151 @@
<template>
<view class="bg-[var(--page-bg-color)] min-h-[100vh]" :style="themeColor()">
<mescroll-body ref="mescrollRef" @init="mescrollInit" :down="{ use: false }" height="auto" @up="getListFn"
:top="mescrollTop">
<view class="sidebar-margin body-bottom" v-if="productUnitList.length">
<u-swipe-action class="bg-[#ffffff]" v-for="(item,index) in productUnitList" :key="index">
<u-swipe-action-item :name="item.id" :ref="setSwipeActionRef(index)"
@click="(v) => hanldeOptionCallBack(v, item.id)" :options="btnOptions">
<view class="border-0 border-b-1 border-solid border-[#ccc]">
<view class="p-[20rpx] leading-normal">
<text class="text-[12px] text-[#6a6a6a]">{{item.name}}</text>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
<up-modal :show="show" @confirm="confirm" showCancelButton @cancel="show = false" :title="t('delete')"
:content="t('productUnitDeleteTips')" ref="uModal"></up-modal>
</view>
<mescroll-empty v-if="!productUnitList.length && !loading &&!listLoading"></mescroll-empty>
</mescroll-body>
<loading-page :loading="loading"></loading-page>
<view class="fixed bottom-4 right-4 z-2">
<up-button @click="redirect({ url: '/addon/erp/pages/product/unit/add'})" type="primary" shape="circle"
:customStyle="{borderRadius: '50%',padding: 0,width: '50px',height: '50px'}">
<u-icon size="30" color="#fff" name="plus-circle-fill"></u-icon>
</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { t } from '@/locale';
import { redirect, pxToRpx } from '@/utils/common';
import MescrollBody from '@/components/mescroll/mescroll-body/mescroll-body.vue';
import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
import { getProductUnitList, deleteProductUnit } from '@/addon/erp/api/product';
import { onShow, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
const { downCallback, mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
const show = ref(false);
// 获取系统状态栏的高度
let menuButtonInfo : any = {};
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API尚未兼容)
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
// #endif
const mescrollTop = "20rpx"
const productUnitList = ref<Array<any>>([]),
loading = ref<boolean>(true),
listLoading = ref<boolean>(true),
mescrollRef = ref(null);
interface mescrollStructure {
num : number,
size : number,
endSuccess : Function,
[propName : string] : any
}
const getListFn = (mescroll : mescrollStructure) => {
listLoading.value = true;
let data : Object = {
page: mescroll.num,
limit: mescroll.size
};
getProductUnitList(data).then((res : any) => {
let newArr = res.data.data;
mescroll.endSuccess(newArr.length);
//设置列表数据
if (mescroll.num == 1) {
productUnitList.value = []; //如果是第一页需手动制空列表
}
productUnitList.value = productUnitList.value.concat(newArr);
listLoading.value = false;
loading.value = false;
}).catch((err) => {
// 如果是4001没有绑定企业账号强制跳转绑定
if (err.code == 4001) {
redirect({ url: '/addon/erp/pages/member/bind' })
}
listLoading.value = false;
loading.value = false;
mescroll.endErr(); // 请求失败, 结束加载
})
}
const btnOptions = ref([{
text: t('edit'),
style: {
backgroundColor: '#3c9cff'
}
}, {
text: t('delete'),
style: {
backgroundColor: '#f56c6c'
}
}])
const swipeActionRefs = ref<any[]>([]);
const setSwipeActionRef = (index : number) => (el : any[] | null) => {
if (el) {
swipeActionRefs.value[index] = el;
}
};
// 按钮回调
const del_id = ref<number>(0);
const hanldeOptionCallBack = (obj : any, id : number) => {
// 第一个编辑按钮
if (obj.index === 0) {
redirect({ url: '/addon/erp/pages/product/unit/add', param: { id: id } })
}
// 第二个删除按钮
if (obj.index === 1) {
show.value = true;
del_id.value = id;
}
};
// 确认删除
const confirm = () => {
if (del_id.value == 0) {
uni.showToast({ icon: 'none', title: t("Invalid ID") });
return false;
}
let index = productUnitList.value.findIndex(item => item.id === del_id.value);
deleteProductUnit(del_id.value)
.then(() => {
productUnitList.value.splice(index, 1);
del_id.value = 0;
show.value = false;
if (swipeActionRefs.value[index]) {
swipeActionRefs.value[index].closeHandler();
}
}).catch(() => { });
}
</script>
<style>
</style>