feat(uniapp_v2): 二开功能迁移与小程序主包优化

- 从 uniapp 迁移 HJF 页面、API、组件及用户/订单相关改动
- queue、assets 使用独立分包以降低主包体积
- 修复首页单根节点与支付结果页 v-if 链
- 关闭 HjfDemoPanel 全局注册;uniNoticeBar 注释 $getAppWebview 避免 __webviewId__ 报错
- 配置域名与 manifest 应用名称;cache/store 防御性处理

Made-with: Cursor
This commit is contained in:
apple
2026-03-26 12:16:01 +08:00
parent c84aeda062
commit 8e17762510
742 changed files with 184117 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
<script>
import baseDrawer from '@/components/tui-drawer/tui-drawer.vue';
export default {
props: {
visible: {
type: Boolean,
default: false,
},
cateList:{
type:Array,
default: () => []
},
brandList:{
type:Array,
default: () => []
},
},
data(){
return {
cateGoryList:[],
brand_id: [],
cateId:[],
sid:''
}
},
watch:{
cateList:{
handler(val){
this.cateGoryList = val;
this.cateGoryList.map(item=>{
this.$set(item,'more', true);
})
},
immediate: true
},
},
computed: {
fixedTop() {
// #ifdef MP || APP-PLUS
return uni.getWindowInfo().statusBarHeight
// #endif
// #ifdef H5
return 20
// #endif
},
},
components: {
baseDrawer
},
methods: {
closeDrawer() {
this.$emit('closeDrawer');
},
checkBrand(item){
if(this.brand_id.includes(item.id)){
this.brand_id = this.brand_id.filter(function (ele){return ele != item.id;});
}else{
this.brand_id.push(item.id);
}
},
checkCate(item){
this.sid = item.id;
},
checkMore(item){
item.more = !item.more
},
confirmFilter(){
let data = {
brand_id: this.brand_id.join(','),
sid: this.sid
};
this.$emit('filterChange',data);
},
resetFilter(){
this.brand_id = [];
this.sid = '';
let data = {
brand_id: '',
sid: ''
};
this.$emit('filterChange',data);
}
}
}
</script>
<template>
<view>
<base-drawer mode="right" :visible="visible" background-color="transparent" mask maskClosable
@close="closeDrawer">
<view class="drawer_box bg--w111-fff px-32 h-full">
<scroll-view scroll-y="true" style="height: 100vh;">
<view :style="{height:fixedTop + 'px'}"></view>
<view class="h-80 flex-center fs-34 fw-500 text--w111-333">筛选</view>
<view class="activity_box py-24">
<view v-if="brandList.length">
<view class="fs-28 text--w111-333 fw-500 mt-24">品牌</view>
<view class="grid-column-3 box_gap mt-24">
<view class="h-56 rd-28rpx bg--w111-f5f5f5 flex-center fs-24 text--w111-333"
v-for="item in brandList" :key="item.id"
:class="{active: brand_id.includes(item.id)}"
@tap="checkBrand(item)">
<text class="inline-block w-full line1 px-12 text-center">{{item.brand_name}}</text>
</view>
</view>
</view>
<view v-if="cateGoryList.length">
<view class="fs-28 text--w111-333 fw-500 mt-48">分类</view>
<view v-for="(item,i) in cateGoryList" :key="i">
<view class="flex-between-center mt-48">
<text class='fs-24'>{{item.cate_name}}</text>
<view class="flex-y-center fs-20 text--w111-999 lh-34rpx" @tap="checkMore(item)">
<text>{{ item.more ? '收起' : '展开' }}</text>
<text class="iconfont fs-20" :class="item.more ? 'icon-ic_uparrow' : 'icon-ic_downarrow'"></text>
</view>
</view>
<view class="grid-column-3 box_gap mt-24" v-show="item.more">
<view class="h-56 rd-28rpx bg--w111-f5f5f5 flex-center fs-24 text--w111-333"
v-for="(cate,k) in item.children" :key="k"
:class="{active: cate.id == sid}"
@tap="checkCate(cate)">
<text class="inline-block w-full line1 px-12 text-center">{{cate.cate_name}}</text>
</view>
</view>
</view>
</view>
</view>
<view class="pb-safe">
<view class="h-112"></view>
</view>
</scroll-view>
<view class="fixed-lb pb-safe bg--w111-fff w-full">
<view class="px-32 flex-between-center h-112">
<view class="w-296 h-72 rd-40rpx flex-center text-mer con_border bg--w111-fff"
@tap="resetFilter()">重置</view>
<view class="w-296 h-72 rd-40rpx flex-center text--w111-fff mer-bg" @tap="confirmFilter()">确定</view>
</view>
</view>
</view>
</base-drawer>
</view>
</template>
<style lang="scss">
.drawer_box {
width: 668rpx;
border-radius: 40rpx 0 0 40rpx;
overflow: auto;
}
.box_gap {
grid-row-gap: 24rpx;
grid-column-gap: 26rpx;
}
.con_border{
border: 1rpx solid $primary-merchant;
}
.active{
border: 1px solid $primary-merchant;
color: $primary-merchant;
background: $light-primary-merchant;
}
.text-mer{
color: $primary-merchant;
}
</style>

View File

@@ -0,0 +1,46 @@
<script>
import baseDrawer from '@/components/tui-drawer/tui-drawer.vue';
export default {
props: {
visible: {
type: Boolean,
default: false
},
ensureInfo: {
type: Object,
default: () => {}
}
},
components: {
baseDrawer
},
methods: {
closeDrawer() {
this.$emit('closeDrawer');
}
}
};
</script>
<template>
<view>
<base-drawer mode="bottom" :visible="visible" background-color="transparent" mask maskClosable @close="closeDrawer">
<view class="w-full bg--w111-fff rd-t-40rpx py-32">
<view class="text-center fs-32 text--w111-333 fw-500 mb-34">服务</view>
<scroll-view scroll-y="true" class="h-400">
<view class="px-32">
<view class="mb-38" v-for="(item, index) in ensureInfo.ensure" :key="index">
<view class="flex-y-center">
<image class="w-34 h-34" :src="item.image"></image>
<text class="pl-12 text--w111-333 fs-28 fw-500">{{ item.name }}</text>
</view>
<view class="mt-6 pl-40 fs-22 text--w111-999">{{ item.desc }}</view>
</view>
</view>
</scroll-view>
<view class="mx-20 pb-safe">
<view class="mt-52 h-72 flex-center rd-36px mer-bg fs-26 text--w111-fff" @click="closeDrawer">确定</view>
</view>
</view>
</base-drawer>
</view>
</template>

View File

@@ -0,0 +1,134 @@
<template>
<base-drawer mode="bottom" :visible="attr.cartAttr" background-color="transparent" zIndex="3000" mask maskClosable
@close="closeDrawer">
<view>
<view class="bg--w111-fff rd-t-40rpx">
<view class="w-full pt-32">
<view class="px-32 flex">
<image class="w-180 h-180 rd-16rpx" :src="attr.productSelect.image" @tap="showImg"></image>
<view class="pl-24">
<baseMoney :money="attr.productSelect.channel_price" symbolSize="32" integerSize="48"
decimalSize="32" color="#FF7E00" weight></baseMoney>
<view class="mt-20 fs-24 text--w111-999">库存:{{ attr.productSelect.stock }}</view>
</view>
</view>
</view>
<scroll-view class="px-32" scroll-y="true" style="max-height: 400rpx;">
<view class="item mt-32" v-for="(item, indexw) in attr.productAttr" :key="indexw">
<view class="fs-28">{{ item.attr_name }}</view>
<view class="flex-y-center flex-wrap">
<view class="sku-item" :class="item.index === itemn.attr ? 'active' : ''"
v-for="(itemn, indexn) in item.attr_value" @click="tapAttr(indexw, indexn)"
:key="indexn">
{{ itemn.attr }}
</view>
</view>
</view>
</scroll-view>
<view class="flex-between-center px-32 mt-24">
<text class="fs-28">数量</text>
<view class="flex-y-center">
<view class="jia-btn w-84 h-48 flex-center" @click="CartNumAdd(false)">
<text class="iconfont icon-ic_Reduce fs-24"></text>
</view>
<view class='w-84 h-48 text-center lh-48rpx bg--w111-f5f5f5'>{{ attr.productSelect.cart_num }}</view>
<view class="jia-btn w-84 h-48 flex-center" @click="CartNumAdd(true)">
<text class="iconfont icon-ic_increase fs-24"></text>
</view>
</view>
</view>
<view class="mx-20 pb-box">
<view class="mt-52 h-72 flex-center rd-36px mer-bg fs-26 text--w111-fff" @click="confirmCartAdd"
v-if="attr.productSelect.stock">
确定</view>
<view class="mt-52 h-72 flex-center rd-36px bg-gray fs-26 text--w111-fff" v-else>已售罄</view>
</view>
</view>
</view>
</base-drawer>
</template>
<script>
export default {
props:{
attr: {
type: Object,
default: () => {}
},
storeInfo: {
type: Object,
default: () => {}
},
},
data(){
return {
}
},
methods:{
CartNumAdd(type){
this.$emit('ChangeCartNum', type);
},
closeDrawer(){
this.$emit('myevent');
},
confirmCartAdd(){
this.$emit('onConfirm');
},
tapAttr: function(indexw, indexn) {
let that = this;
that.$emit("attrVal", {
indexw: indexw,
indexn: indexn
});
this.$set(this.attr.productAttr[indexw], 'index', this.attr.productAttr[indexw].attr_values[indexn]);
let value = that
.getCheckedValue()
.join(",");
that.$emit("ChangeAttr", value);
},
//获取被选中属性;
getCheckedValue: function() {
let productAttr = this.attr.productAttr;
let value = [];
for (let i = 0; i < productAttr.length; i++) {
for (let j = 0; j < productAttr[i].attr_values.length; j++) {
if (productAttr[i].index === productAttr[i].attr_values[j]) {
value.push(productAttr[i].attr_values[j]);
}
}
}
return value;
},
showImg(){
this.$emit('getImg');
}
}
}
</script>
<style lang="scss">
.sku-item {
height: 56rpx;
line-height: 56rpx;
border: 1px solid #F2F2F2;
font-size: 24rpx;
color: #333;
padding: 0 44rpx;
border-radius: 28rpx;
margin: 24rpx 0 0 16rpx;
background-color: #F2F2F2;
word-break: break-all;
}
.active {
color: $primary-merchant;
background: $light-primary-merchant;
border-color: $primary-merchant;
}
.pb-box{
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.bg-gray{
background-color: #ccc;
}
</style>

View File

@@ -0,0 +1,45 @@
<template>
<!-- 产品参数 -->
<base-drawer mode="bottom" :visible="specsInfo.show" background-color="transparent" mask maskClosable @close="closeSpecs">
<view class="w-full bg--w111-fff rd-t-40rpx py-32">
<view class="text-center fs-32 text--w111-333 fw-500 mb-34">参数</view>
<scroll-view scroll-y="true" class="h-400">
<view class="px-32 scroll-content">
<view class="item flex break_word" v-for="(item,index) in specsInfo.specs" :key="index">
<view class="w-160 text--w111-999 mr-12">{{item.name}}</view>
<view class="flex-1">{{item.value}}</view>
</view>
</view>
</scroll-view>
<view class="mx-20 pb-safe">
<view class="mt-52 h-72 flex-center rd-36px mer-bg fs-26 text--w111-fff" @click="closeSpecs">确定</view>
</view>
</view>
</base-drawer>
</template>
<script>
import baseDrawer from '@/components/tui-drawer/tui-drawer.vue';
export default {
props: {
specsInfo: {
type: Object,
default: () => {}
},
},
components: {
baseDrawer
},
methods: {
closeSpecs() {
this.$emit('myevent');
}
}
}
</script>
<style scoped lang="scss">
.item ~ .item{
margin-top: 40rpx;
}
</style>

View File

@@ -0,0 +1,128 @@
<template>
<!-- 底部导航 -->
<keep-alive>
<view class="page-footer">
<view class="foot-item" :class="item.pagePath == activeRouter?'active':''"
v-for="(item,index) in footerList" :key="index" @click="goRouter(item)">
<block v-if="item.pagePath == activeRouter">
<image :src="item.selectedIconPath"></image>
<view class="txt">{{item.text}}</view>
</block>
<block v-else>
<image :src="item.iconPath"></image>
<view class="txt">{{item.text}}</view>
</block>
<uni-badge v-if="index == 1 && cartNum > 0" class="badge-style" :text="cartNum" absolute="rightTop"></uni-badge>
</view>
</view>
</keep-alive>
</template>
<script>
import { mapGetters } from 'vuex';
import {getCartCounts} from '@/api/order.js';
export default {
name: 'footer',
props: {},
created() {
let routes = getCurrentPages(); //获取当前打开过的页面路由数组
let curRoute = routes[routes.length - 1].route //获取当前页面路由
this.activeRouter = '/' + curRoute
},
computed: {
...mapGetters(['isLogin', 'cartNum']),
styleType(){
return this.$store.state.app.system_channel_style;
}
},
data() {
return {
activeRouter:'',
footerList:[
{
pagePath: "/pages/merchant/index/index",
iconPath: require("../../static/1-0.png"),
selectedIconPath: require("../../static/1-1.png"),
text: "首页"
},
{
pagePath: "/pages/merchant/cart/index",
iconPath: require("../../static/2-0.png"),
selectedIconPath: require("../../static/2-1.png"),
text: "购物车"
},
{
pagePath: "/pages/merchant/user/index",
iconPath: require("../../static/3-0.png"),
selectedIconPath: require("../../static/3-1.png"),
text: "我的"
}
],
// cartNum:0
}
},
mounted() {
this.getCartNum();
},
methods: {
goRouter(item) {
var pages = getCurrentPages();
var page = (pages[pages.length - 1]).$page.fullPath;
if (item.pagePath == page) return
uni.redirectTo({
url: item.pagePath,
animationType: 'none' // 关闭默认的滑动效果
});
},
getCartNum() {
this.$store.dispatch('indexData/getCartNum')
},
}
}
</script>
<style scoped lang="scss">
.page-footer {
position: fixed;
bottom: 0;
left:0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: space-around;
width: 100%;
height: calc(100rpx + constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
height: calc(100rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
box-sizing: border-box;
border-top: solid 1rpx #F3F3F3;
background-color: #fff;
padding-bottom: constant(safe-area-inset-bottom); ///兼容 IOS<11.2/
padding-bottom: env(safe-area-inset-bottom); ///兼容 IOS>11.2/
.foot-item {
display: flex;
width: max-content;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
padding: 0 20rpx;
&.active {
color: $primary-merchant;
}
}
.foot-item image {
height: 40rpx;
width: 40rpx;
text-align: center;
margin: 0 auto;
}
.foot-item .txt {
font-size: 20rpx;
margin-top: 6rpx;
}
}
</style>

View File

@@ -0,0 +1,11 @@
import CryptoJS from './crypto-js.js'
/**
* @word 要加密的内容
* @keyWord String 服务器随机返回的关键字
* */
export function aesEncrypt(word,keyWord="XwKsGlMcdPMEhR1B"){
var key = CryptoJS.enc.Utf8.parse(keyWord);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return encrypted.toString();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long