Ver código fonte

feat 管理端页面

wzh 4 meses atrás
pai
commit
720450916e

+ 177 - 0
api/hexiao.js

@@ -0,0 +1,177 @@
+import request from '@/utils/requestAdmin'
+
+
+//返送验证码
+export function getVerificationCode(tel,userType){
+    return request({
+        url: `/miniapp/sendYzm?tel=`+ tel+"&userType="+userType,
+        method: 'post',
+    })
+}
+
+
+
+//登录
+export function login(tel,code,userType,jsCode){
+    return request({
+        url: `/miniapp/login`,
+        method: 'post',
+        params: {
+            phone:tel,
+            userType:userType,
+            code:code,
+            jsCode
+        }
+    })
+}
+
+export function getAdminUserInfo(){
+    return request({
+        url: `/miniapp/getAdminUserInfo`,
+        method: 'post',
+    })
+}
+
+export function getQrcodeNum(code){
+    return request({
+        url: `/ywy/getQrcodeNum`,
+        method: 'post',
+        params: {
+            qrCode:code
+        }
+    })
+}
+
+
+
+export function active(data){
+    return request({
+        url: `/ywy/active`,
+        method: 'post',
+        data: data
+    })
+}
+
+
+export function getRetailDetail(id){
+    return request({
+        url: `/store/show`,
+        method: 'post',
+        params: {
+            id:id
+        }
+    })
+}
+
+export function removeRetail(id){
+    return request({
+        url: `/store/delete`,
+        method: 'post',
+        params: {
+            id:id
+        }
+    })
+}
+
+
+
+export function getStoreList(pageIndex,pageSize,storeName){
+    return request({
+        url: `/store/queryPage`,
+        method: 'post',
+        params: {
+            pageIndex:pageIndex,
+            pageSize:pageSize,
+            storeName:storeName
+        }
+    })
+}
+
+export function addStore(store_name,contact_name,contact_phone,address){
+    return request({
+        url: `/store/save`,
+        method: 'post',
+        params: {
+            store_name:store_name,
+            contact_name:contact_name,
+            contact_phone:contact_phone,
+            address:address
+        }
+
+    })
+}
+export function updateStore(id,store_name,contact_name,contact_phone,address){
+    return request({
+        url: `/store/update`,
+        method: 'post',
+        params: {
+            id:id,
+            store_name:store_name,
+            contact_name:contact_name,
+            contact_phone:contact_phone,
+            address:address
+        }
+
+    })
+}
+/**
+ * 查询上货记录
+ * @param pageIndex
+ * @param pageSize
+ * @returns {*}
+ */
+export function queryActiveRecord(pageIndex,pageSize){
+    return request({
+        url: `/ywy/queryActiveRecord`,
+        method: 'post',
+        params: {
+            pageIndex:pageIndex,
+            pageSize:pageSize
+        }
+    })
+}
+
+
+
+
+export function ywyList(keyword){
+    return request({
+        url: `/jxs/jxsRetailListQuery`,
+        method: 'post',
+        params: {
+            keyword:keyword
+        }
+    })
+}
+
+export function areaList(keyword){
+    return request({
+        url: `/jxs/jxsAreaList`,
+        method: 'post',
+    })
+}
+
+export function addYwy(data){
+    return request({
+        url: `/jxs/jxsSaveRetail`,
+        method: 'post',
+        data: data
+    })
+}
+
+export function updateYwy(data){
+    return request({
+        url: `/jxs/jxsUpdateYwy`,
+        method: 'post',
+        data: data
+    })
+}
+export function delYwy(id){
+    return request({
+        url: `/jxs/deleteYwy`,
+        method: 'post',
+        params: {
+            ywyId:id
+        }
+    })
+}

+ 263 - 0
components/cjx/select-store-drawer.vue

@@ -0,0 +1,263 @@
+<template>
+  <view v-if="show" class="drawer-wrapper" @touchmove.stop.prevent>
+    <view class="drawer-overlay" @click="close"></view>
+    <view class="drawer-content">
+      <view class="drawer-header">
+        <text class="title">选择门店</text>
+        <uni-icons class="close-icon" type="closeempty" size="22" color="#999" @click="close"></uni-icons>
+      </view>
+
+      <view class="search-bar">
+        <uni-icons type="search" size="20" color="#999"></uni-icons>
+        <input
+            class="search-input"
+            v-model="searchQuery"
+            placeholder="输入门店名称"
+            placeholder-class="placeholder"
+            confirm-type="search"
+            @confirm="handleSearch"
+        />
+      </view>
+
+      <scroll-view class="store-list-scroll" scroll-y="true" @scrolltolower="handleScrollToLower">
+        <view v-if="storeList.length === 0 && loadStatus !== 'loading'" class="empty-tip">
+          暂无门店数据
+        </view>
+        <view
+            v-for="store in storeList"
+            :key="store.id"
+            class="store-card"
+            :class="{ 'selected': store.id === selectedId }"
+            @click="handleStoreSelect(store)"
+        >
+          <view class="card-header">
+            <uni-icons type="shop-filled" size="20" color="#3c82f8"></uni-icons>
+            <text class="store-name">{{ store.store_name }}</text>
+          </view>
+          <view class="info-row">
+            <text class="info-label">店主名称:</text>
+            <text class="info-value">{{ store.contact_name }}</text>
+          </view>
+          <view class="info-row">
+            <text class="info-label">联系方式:</text>
+            <text class="info-value">{{ store.contact_phone }}</text>
+          </view>
+          <view class="info-row">
+            <text class="info-label">门店地址:</text>
+            <text class="info-value">{{ store.address }}</text>
+          </view>
+        </view>
+        <uni-load-more :status="loadStatus"></uni-load-more>
+      </scroll-view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getStoreList } from '@/api/hexiao.js';
+export default {
+  name: "select-store-drawer",
+  props: {
+    // 控制抽屉显示
+    show: {
+      type: Boolean,
+      default: false
+    },
+    // 当前选中的门店ID,用于高亮显示
+    selectedId: {
+      type: [Number, String],
+      default: null
+    }
+  },
+  data() {
+    return {
+      searchQuery: '',
+      storeList: [],
+      pagination: {
+        page: 1,
+        limit: 10,
+      },
+      // 加载状态: 'loading'-加载中, 'more'-有更多, 'noMore'-没有更多了
+      loadStatus: 'more',
+      isLoading: false,
+    };
+  },
+  watch: {
+    // 监听 show 属性的变化
+    show(newVal) {
+      if (newVal && this.storeList.length === 0) {
+        // 当抽屉第一次打开时,加载初始数据
+        this.fetchStoreList(true);
+      }
+    }
+  },
+  methods: {
+    // 核心:获取门店列表数据
+    fetchStoreList(isRefresh = false) {
+      if (this.isLoading || (this.loadStatus === 'noMore' && !isRefresh)) {
+        return; // 防止重复加载
+      }
+
+      if (isRefresh) {
+        this.pagination.page = 1;
+        this.storeList = [];
+        this.loadStatus = 'more';
+      }
+
+      this.isLoading = true;
+      this.loadStatus = 'loading';
+
+      // --- [模拟API请求] ---
+      // 在这里替换成您真实的 uni.request API 调用
+      console.log(`正在请求第 ${this.pagination.page} 页数据, 搜索词: "${this.searchQuery}"`);
+
+      getStoreList(this.pagination.page,this.pagination.limit,this.searchQuery).then(res=>{
+        let data = res.data;
+        let mockData = data.records;
+        if (mockData.length > 0) {
+          this.storeList = [...this.storeList, ...mockData];
+          this.pagination.page++;
+          this.loadStatus = mockData.length < this.pagination.limit ? 'noMore' : 'more';
+        } else {
+          this.loadStatus = 'noMore';
+        }
+        this.isLoading = false;
+      });
+    },
+
+    // 滚动到底部加载更多
+    handleScrollToLower() {
+      this.fetchStoreList();
+    },
+
+    // 点击搜索按钮
+    handleSearch() {
+      this.fetchStoreList(true);
+    },
+
+    // 选择了一个门店
+    handleStoreSelect(store) {
+      // 通过 $emit 将选中的门店数据传递给父组件
+      this.$emit('select', store);
+      this.close();
+    },
+
+    // 关闭抽屉
+    close() {
+      // 通知父组件关闭
+      this.$emit('close');
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.drawer-wrapper {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+}
+
+.drawer-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.4);
+}
+
+.drawer-content {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 85vh; // 抽屉高度
+  background-color: #f5f6fa;
+  border-top-left-radius: 20rpx;
+  border-top-right-radius: 20rpx;
+  display: flex;
+  flex-direction: column;
+}
+
+.drawer-header {
+  position: relative;
+  text-align: center;
+  padding: 30rpx 0;
+  .title {
+    font-size: 32rpx;
+    font-weight: bold;
+  }
+  .close-icon {
+    position: absolute;
+    right: 30rpx;
+    top: 50%;
+    transform: translateY(-50%);
+  }
+}
+
+.search-bar {
+  display: flex;
+  align-items: center;
+  background-color: #ffffff;
+  border-radius: 50rpx;
+  padding: 0 25rpx;
+  height: 70rpx;
+  margin: 0 30rpx 20rpx;
+
+  .search-input {
+    flex: 1;
+    font-size: 28rpx;
+    margin-left: 15rpx;
+  }
+  .placeholder {
+    color: #b0b0b0;
+  }
+}
+
+.store-list-scroll {
+  flex: 1;
+  height: 100%;
+  padding: 0 30rpx;
+}
+
+.empty-tip {
+  text-align: center;
+  color: #999;
+  padding-top: 100rpx;
+}
+
+.store-card {
+  background-color: #ffffff;
+  border-radius: 16rpx;
+  margin-bottom: 20rpx;
+  padding: 30rpx;
+  border: 2rpx solid transparent; // 占位边框
+  transition: border-color 0.2s;
+
+  &.selected {
+    border-color: #3c82f8; // 选中时的蓝色边框
+  }
+
+  .card-header {
+    display: flex;
+    align-items: center;
+    .store-name {
+      font-size: 30rpx;
+      font-weight: bold;
+      color: #333;
+      margin-left: 15rpx;
+    }
+  }
+
+  .info-row {
+    margin-top: 15rpx;
+    font-size: 26rpx;
+    .info-label { color: #999; }
+    .info-value { color: #333; }
+  }
+}
+</style>

+ 87 - 0
components/cjx/tabbar_hexiao_jxs.vue

@@ -0,0 +1,87 @@
+<template>
+  <view class="tabbar-container">
+    <view class="tabbar-item" @click="navigate('/pages/cjx/hexiao/jxs/index', 0)">
+      <uni-icons type="home-filled" :color="current === 0 ? activeColor : inactiveColor" size="26"></uni-icons>
+      <view class="text" :style="{ color: current === 0 ? activeColor : inactiveColor }">首页</view>
+    </view>
+
+    <view class="tabbar-item" @click="navigate('/pages/cjx/hexiao/jxs/order', 1)">
+      <uni-icons type="shop" :color="current === 1 ? activeColor : inactiveColor" size="26"></uni-icons>
+      <view class="text" :style="{ color: current === 1 ? activeColor : inactiveColor }">订单</view>
+    </view>
+
+    <view class="tabbar-item" @click="navigate('/pages/cjx/hexiao/jxs/my', 2)">
+      <uni-icons type="person" :color="current === 2 ? activeColor : inactiveColor" size="26"></uni-icons>
+      <view class="text" :style="{ color: current === 2 ? activeColor : inactiveColor }">我的</view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: "my-tab-bar",
+  props: {
+    // 通过 props 接收当前页面的索引,用于高亮
+    // 0: 首页, 1: 门店, 2: 我的
+    current: {
+      type: Number,
+      required: true
+    }
+  },
+  data() {
+    return {
+      // 定义高亮和非高亮颜色
+      activeColor: '#409EFF', // 蓝色
+      inactiveColor: '#909399' // 灰色
+    };
+  },
+  methods: {
+    navigate(path, index) {
+      // 如果点击的已经是当前页,则不跳转,防止重复加载
+      if (this.current === index) {
+        return;
+      }
+
+      // 使用 reLaunch 跳转可以关闭所有其他页面,适合tabBar之间的切换
+      uni.redirectTo({
+        url: path
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tabbar-container {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100rpx;
+  background-color: #ffffff;
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+  border-top: 1rpx solid #f0f0f0;
+  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.03);
+
+  /* 适配iPhone X等机型的底部安全区域 */
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
+  z-index: 999;
+}
+
+.tabbar-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  flex: 1; // 保证均分
+  height: 100%;
+}
+
+.text {
+  font-size: 24rpx;
+  margin-top: 2rpx;
+}
+</style>

+ 87 - 0
components/cjx/tabbar_hexiao_ywy.vue

@@ -0,0 +1,87 @@
+<template>
+  <view class="tabbar-container">
+    <view class="tabbar-item" @click="navigate('/pages/cjx/hexiao/ywy/index', 0)">
+      <uni-icons type="home-filled" :color="current === 0 ? activeColor : inactiveColor" size="26"></uni-icons>
+      <view class="text" :style="{ color: current === 0 ? activeColor : inactiveColor }">首页</view>
+    </view>
+
+    <view class="tabbar-item" @click="navigate('/pages/cjx/hexiao/ywy/retail', 1)">
+      <uni-icons type="shop" :color="current === 1 ? activeColor : inactiveColor" size="26"></uni-icons>
+      <view class="text" :style="{ color: current === 1 ? activeColor : inactiveColor }">门店</view>
+    </view>
+
+    <view class="tabbar-item" @click="navigate('/pages/cjx/hexiao/ywy/my', 2)">
+      <uni-icons type="person" :color="current === 2 ? activeColor : inactiveColor" size="26"></uni-icons>
+      <view class="text" :style="{ color: current === 2 ? activeColor : inactiveColor }">我的</view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: "my-tab-bar",
+  props: {
+    // 通过 props 接收当前页面的索引,用于高亮
+    // 0: 首页, 1: 门店, 2: 我的
+    current: {
+      type: Number,
+      required: true
+    }
+  },
+  data() {
+    return {
+      // 定义高亮和非高亮颜色
+      activeColor: '#409EFF', // 蓝色
+      inactiveColor: '#909399' // 灰色
+    };
+  },
+  methods: {
+    navigate(path, index) {
+      // 如果点击的已经是当前页,则不跳转,防止重复加载
+      if (this.current === index) {
+        return;
+      }
+
+      // 使用 reLaunch 跳转可以关闭所有其他页面,适合tabBar之间的切换
+      uni.redirectTo({
+        url: path
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tabbar-container {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100rpx;
+  background-color: #ffffff;
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+  border-top: 1rpx solid #f0f0f0;
+  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.03);
+
+  /* 适配iPhone X等机型的底部安全区域 */
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
+  z-index: 999;
+}
+
+.tabbar-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  flex: 1; // 保证均分
+  height: 100%;
+}
+
+.text {
+  font-size: 24rpx;
+  margin-top: 2rpx;
+}
+</style>

+ 243 - 0
pages/cjx/hexiao/jxs/add_ywy.vue

@@ -0,0 +1,243 @@
+<template>
+  <view class="page-container">
+    <view class="form-card">
+
+      <view class="form-item">
+        <text class="form-label">业务员名称</text>
+        <input
+            class="form-input"
+            v-model="formData.name"
+            placeholder="请输入业务员姓名"
+            placeholder-class="placeholder"
+        />
+      </view>
+
+      <view class="form-item">
+        <text class="form-label">业务员账号</text>
+        <input
+            class="form-input"
+            type="number"
+            maxlength="11"
+            v-model="formData.account"
+            placeholder="请输入业务员账号(手机号)"
+            placeholder-class="placeholder"
+        />
+      </view>
+
+      <view class="form-item item-flex" v-if="formData.id != 0">
+        <text class="form-label">业务员状态</text>
+        <view class="switch-wrapper">
+          <text :class="formData.isJob ? 'status-active' : ''">在职</text>
+          <switch :checked="formData.isJob" @change="statusChange" color="#409eff" />
+        </view>
+      </view>
+
+      <picker mode="selector" :range="areaList" range-key="area_name" @change="handleAreaChange">
+        <view class="form-item item-flex">
+          <text class="form-label">所属区域</text>
+          <view class="picker-value">
+            <text :class="{'placeholder': !formData.region}">
+              {{ formData.region || '请选择' }}
+            </text>
+            <uni-icons type="right" size="16" color="#c0c0c0"></uni-icons>
+          </view>
+        </view>
+      </picker>
+
+    </view>
+
+    <view class="footer-save-button">
+      <button class="save-btn" @click="submitForm">保 存</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import {areaList,addYwy,updateYwy,getYwyDetail} from "@/api/hexiao";
+
+export default {
+  data() {
+    return {
+      // 区域列表数据
+      areaList: [
+      ],
+      // 表单数据
+      formData: {
+        id:0,
+        name: '',
+        account: '',
+        isJob: true,
+        region: '', // 用于显示选择的区域名称
+        regionId: null // 用于提交给后端的区域ID
+      }
+    };
+  },
+  onLoad(opt) {
+    if(opt.id){
+      getYwyDetail(opt.id).then(res=>{
+        let data = res.data;
+      })
+    }
+    // 在实际项目中,您应该在这里通过API获取区域列表
+    this.fetchAreaList();
+  },
+  methods: {
+
+    fetchAreaList () {
+      // 模拟区域列表数据
+      areaList().then(res=>{
+        this.areaList = res.data;
+      })
+    },
+    // 监听 Switch 开关变化
+    statusChange(e) {
+      this.formData.isJob = e.detail.value;
+    },
+
+    // 关键改动:处理 Picker 的 change 事件
+    handleAreaChange(e) {
+      const selectedIndex = e.detail.value; // 获取用户选择的索引
+      const selectedArea = this.areaList[selectedIndex]; // 根据索引找到对应的区域对象
+
+      console.log('选择的区域对象:', selectedArea);
+
+      // 更新表单数据
+      this.formData.region = selectedArea.area_name;
+      this.formData.regionId = selectedArea.id;
+    },
+
+    // 提交表单
+    submitForm() {
+      // --- 手动进行数据校验 ---
+      if (!this.formData.name) {
+        uni.showToast({ title: '请输入业务员姓名', icon: 'none' });
+        return;
+      }
+      if (!this.formData.account) {
+        uni.showToast({ title: '请输入业务员账号', icon: 'none' });
+        return;
+      }
+      if (!/^1[3-9]\d{9}$/.test(this.formData.account)) {
+        uni.showToast({ title: '请输入正确的手机号码', icon: 'none' });
+        return;
+      }
+      if (!this.formData.regionId) { // 校验ID是否存在
+        uni.showToast({ title: '请选择所属区域', icon: 'none' });
+        return;
+      }
+
+      console.log('校验通过,准备提交的数据:', this.formData);
+
+      // 执行提交逻辑
+      uni.showLoading({ title: '正在保存...' });
+
+      let data  = {
+        name: this.formData.name,
+        phone:this.formData.account,
+        status: this.formData.status,
+        areaId: this.formData.regionId,
+      }
+      if(this.formData.id == 0){
+        addYwy(data).then(res=>{
+          uni.hideLoading();
+          uni.showToast({
+            title: '保存成功',
+            icon: 'success'
+          });
+          uni.navigateBack();
+        })
+      }else{
+        updateYwy(data).then(res=>{
+          uni.hideLoading();
+          uni.showToast({
+            title: '保存成功',
+            icon: 'success'
+          });
+          uni.navigateBack();
+        })
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+/* 所有样式与上一版完全相同,无需改动 */
+.page-container {
+  min-height: 100vh;
+  background: linear-gradient(to bottom, #e4efff, #f5f6fa 40%);
+  padding: 30rpx;
+  box-sizing: border-box;
+}
+.form-card {
+  background-color: #ffffff;
+  border-radius: 20rpx;
+  padding: 0 40rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+}
+.form-item {
+  padding: 30rpx 0;
+  border-bottom: 1rpx solid #f0f0f0;
+  &:last-child {
+    border-bottom: none;
+  }
+  &.item-flex {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+}
+.form-label {
+  font-size: 30rpx;
+  color: #333;
+  font-weight: 500;
+}
+.form-input {
+  margin-top: 20rpx;
+  font-size: 28rpx;
+  color: #333;
+}
+.placeholder {
+  color: #c0c4cc;
+}
+.switch-wrapper {
+  display: flex;
+  align-items: center;
+  font-size: 28rpx;
+  color: #999;
+  .status-active {
+    color: #409eff;
+  }
+  switch {
+    margin-left: 20rpx;
+    transform: scale(0.8);
+  }
+}
+.picker-value {
+  display: flex;
+  align-items: center;
+  font-size: 28rpx;
+}
+.footer-save-button {
+  position: fixed;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  padding: 20rpx 30rpx;
+  background-color: #f5f6fa;
+  box-sizing: border-box;
+  padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+}
+.save-btn {
+  background-color: #409eff;
+  color: #ffffff;
+  border-radius: 50rpx;
+  font-size: 32rpx;
+  height: 90rpx;
+  line-height: 90rpx;
+  &::after {
+    border: none;
+  }
+}
+</style>

+ 308 - 0
pages/cjx/hexiao/jxs/index.vue

@@ -0,0 +1,308 @@
+<template>
+  <view class="page-container">
+    <view class="page-title">
+      首页
+    </view>
+    <view class="header-section">
+      <view class="greeting-text">{{ userInfo.role }} {{ userInfo.name }}</view>
+      <view class="title-text">提供的服务</view>
+    </view>
+
+    <view class="content-grid">
+      <view class="card large-card" style="background: none;background-color: #54CFAB" @click="build">
+        <view class="large-card-text">
+          <view class="card-title" style="font-size: 40rpx">销售查询</view>
+          <view class="card-subtitle">销售数据一览</view>
+          <view class="scan-button" style="color:#54CFAB">去查看</view>
+        </view>
+        <view class="large-card-icon">
+          <image :src="imgurl+'/cjx/hexiao/xiaoshouchaxun.png'" mode="aspectFit"></image>
+        </view>
+      </view>
+
+      <view class="card" @click="navigateTo('ywyList')">
+        <view class="icon-wrapper" style="background-color: #e4f7ed;">
+          <image :src="imgurl+'/cjx/hexiao/yewuyuan.png'" mode="aspectFit"></image>
+        </view>
+        <view class="card-title">业务员管理</view>
+      </view>
+
+      <view class="card" @click="navigateTo('patrolRecords')">
+        <view class="icon-wrapper" style="background-color: #f2e7ff;">
+          <image :src="imgurl+'/cjx/hexiao/hexiaojilu_jxs.png'" mode="aspectFit"></image>
+        </view>
+        <view class="card-title">核销记录</view>
+      </view>
+
+
+      <view class="card stat-card mendian-card" @click="navigateTo('storeStats')">
+        <view class="stat-content">
+          <view class="stat-title">门店统计</view>
+          <view class="stat-value">{{ stats.storeCount }}</view>
+          <view class="stat-label">总数</view>
+        </view>
+      </view>
+
+      <view class="card stat-card jifen-card" @click="navigateTo('pointsStats')">
+        <view class="stat-content">
+          <view class="stat-title">积分统计</view>
+          <view class="stat-value">{{ formatNumber(stats.pointsCount) }}</view>
+          <view class="stat-label">总数</view>
+        </view>
+      </view>
+    </view>
+    <select-store-drawer
+        :show="showDrawer"
+        :selected-id="selectedStore ? selectedStore.id : null"
+        @close="showDrawer = false"
+        @select="onStoreSelect"
+    ></select-store-drawer>
+    <CustomTabbar :current="0"/>
+  </view>
+
+</template>
+
+<script>
+import CustomTabbar from '@/components/cjx/tabbar_hexiao_jxs.vue';
+import {getAdminInfo} from "../../../../utils/auth";
+import {getAdminUserInfo} from "../../../../api/hexiao";
+import SelectStoreDrawer from '@/components/cjx/select-store-drawer.vue';
+
+export default {
+  components: {
+    CustomTabbar,
+    SelectStoreDrawer
+  },
+  data() {
+    return {
+      selectedStore: 0,
+      showDrawer:false,
+      imgurl:"https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu",
+      userInfo: {
+        name: '',
+        role: '超吉炫'
+      },
+      stats: {
+        storeCount: 12,
+        pointsCount: 1120150
+      }
+    };
+  },
+  onLoad(){
+    let info = getAdminInfo();
+    this.userInfo.role = this.userInfo.role+""+info.roleName;
+    this.userInfo.name = info.userName;
+  },
+  onShow(){
+    getAdminUserInfo().then(res=>{
+      this.stats.storeCount = res.data.storeCount;
+      this.stats.pointsCount = res.data.pointTotal;
+    })
+  },
+  methods: {
+    // 监听从抽屉组件传来的 select 事件
+
+    onStoreSelect(store) {
+      console.log('在首页接收到选中的门店:', store);
+      this.selectedStore = store;
+      uni.navigateTo({ url: '/pages/cjx/hexiao/scan_code?storeId='+store.id });
+    },
+    // 格式化数字,添加千位分隔符
+    formatNumber(num) {
+      return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+    },
+    // 扫码事件
+    scanCode(type) {
+      // uni.navigateTo({ url: `/pages/cjx/hexiao/scan_code` });
+      if("stock" === type){
+        this.showDrawer = true;
+      }else{
+        this.build();
+      }
+    },
+    build(){
+      uni.showToast({
+        title: '功能建设中',
+        icon: 'none'
+      });
+    },
+    // 页面跳转
+    navigateTo(page) {
+      console.log('准备跳转到:', page);
+      // uni.navigateTo({ url: `/pages/${page}/${page}` });
+      if("ywyList" === page){
+        uni.navigateTo({ url: `/pages/cjx/hexiao/jxs/ywy_list` });
+        return;
+      }
+      if("stockRecords" === page){
+        uni.navigateTo({ url: `/pages/cjx/hexiao/ywy/add_goods_record` });
+      }else{
+        this.build();
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.page-container {
+  min-height: 100vh;
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/hexiao_bg.png");
+  background-size: contain;
+  background-position: center;
+  background-repeat: no-repeat;
+}
+.page-title {
+  color: #ffffff;
+  text-align: center;
+  font-size: 19px;
+  height: 160rpx;
+  line-height: 160rpx;
+}
+.header-section {
+  padding: 40rpx 40rpx 80rpx;
+  padding-top: 0rpx;
+  color: #ffffff;
+
+  .greeting-text {
+    font-size: 32rpx;
+    opacity: 0.9;
+  }
+
+  .title-text {
+    font-size: 42rpx;
+    font-weight: bold;
+    margin-top: 66rpx;
+  }
+}
+.content-grid {
+  padding: 0 30rpx;
+  margin-top: -40rpx;
+  position: relative;
+  z-index: 10;
+  // 使用CSS Grid布局
+  display: grid;
+  grid-template-columns: 1fr 1fr; // 两列等宽
+  gap: 20rpx; // 卡片间距
+
+}
+
+.card {
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/mini_bg.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-color: #ffffff;
+  border-radius: 20rpx;
+  padding: 25rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+  display: flex;
+  align-items: center;
+
+  .card-title {
+    font-size: 38rpx;
+    font-weight: 600;
+    color: #333;
+  }
+
+  .icon-wrapper {
+    width: 80rpx;
+    height: 80rpx;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 20rpx;
+  }
+}
+
+.large-card {
+  // 关键:让这个卡片跨越两行
+  grid-row: span 2;
+  height: 83%; // 确保高度自适应
+  position: relative;
+  flex-direction: column;
+  justify-content: space-between;
+  color: #ffffff;
+  background-color: #4cd964;
+
+  .large-card-text {
+    position: relative;
+    left: -18%;
+    top: 16px;
+    .card-title { color: #ffffff; }
+    .card-subtitle { font-size: 24rpx; opacity: 0.8; margin: 10rpx 0; }
+  }
+
+  .scan-button {
+    background-color: #ffffff;
+    color: #5E87FF;
+    font-size: 24rpx;
+    padding: 8rpx 20rpx;
+    border-radius: 30rpx;
+    display: inline-block;
+    margin-top: 20rpx;
+  }
+
+  .large-card-icon {
+    align-self: flex-end;
+    width: 140rpx;
+    height: 140rpx;
+    image { width: 100%; height: 100%; }
+  }
+}
+
+.small-card {
+  flex: 1;
+}
+
+.stat-card {
+  // 关键:让统计卡片默认只占一列,但如果需要跨列可以单独设置
+  // grid-column: span 2; // 如果需要单张卡片占满一行,则打开此行
+
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: flex-end;
+  position: relative;
+  padding-left: 40rpx;
+
+  &::before {
+    content: '';
+    position: absolute;
+    left: 20rpx;
+    top: 35rpx;
+    width: 8rpx;
+    height: 25rpx;
+    background-color: #6a8dff;
+    border-radius: 4rpx;
+  }
+
+  .stat-content {
+    .stat-title { font-size: 28rpx; color: #666; }
+    .stat-value { font-size: 44rpx; font-weight: bold; color: #333; margin: 10rpx 0; }
+    .stat-label { font-size: 24rpx; color: #999; }
+  }
+
+  .stat-image {
+    width: 120rpx;
+    position: absolute;
+    right: 20rpx;
+    bottom: 20rpx;
+  }
+}
+.mendian-card{
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/mendian.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  background-color: inherit;
+
+}
+.jifen-card{
+  background-size: 100% 100%;
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/jifen.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-color: inherit;
+
+}
+</style>

+ 294 - 0
pages/cjx/hexiao/jxs/my.vue

@@ -0,0 +1,294 @@
+<template>
+  <view class="page-container">
+    <view class="profile-header">
+      <view class="avatar-wrapper">
+        <image class="avatar" :src="userInfo.avatarUrl" mode="aspectFill"></image>
+        <view class="badge">{{ userInfo.role }}</view>
+      </view>
+      <view class="phone-number">{{ userInfo.phone }}</view>
+    </view>
+
+    <view class="content-card">
+      <view class="list-item" @click="navigateToAction('points')">
+        <view class="item-left">
+          <image class="item-icon" :src="imgurl+'/cjx/hexiao/my_jifen.png'" mode="aspectFit"></image>
+          <text class="item-label">我的积分</text>
+        </view>
+        <view class="item-right">
+          <text class="item-value">{{ formatNumber(userInfo.points) }}</text>
+<!--          <uni-icons type="right" size="16" color="#c0c0c0"></uni-icons>-->
+        </view>
+      </view>
+
+      <view class="list-item" @click="navigateToAction('shops')">
+        <view class="item-left">
+          <image class="item-icon" :src="imgurl+'/cjx/hexiao/my_mendian.png'" mode="aspectFit"></image>
+          <text class="item-label">我的店铺</text>
+        </view>
+        <view class="item-right">
+          <text class="item-value">{{ userInfo.shopCount }}</text>
+<!--          <uni-icons type="right" size="16" color="#c0c0c0"></uni-icons>-->
+        </view>
+      </view>
+
+<!--      <view class="list-item">-->
+<!--        <view class="item-left">-->
+<!--          <uni-icons type="arrow-up-circle" size="22" color="#666"></uni-icons>-->
+<!--          <text class="item-label">当前版本</text>-->
+<!--        </view>-->
+<!--        <view class="item-right">-->
+<!--          <text class="item-value">{{ appVersion }}</text>-->
+<!--        </view>-->
+<!--      </view>-->
+
+<!--      <view class="list-item" @click="callSupport">-->
+<!--        <view class="item-left">-->
+<!--          <uni-icons type="phone-filled" size="22" color="#666"></uni-icons>-->
+<!--          <text class="item-label">客服电话</text>-->
+<!--        </view>-->
+<!--        <view class="item-right">-->
+<!--          <text class="item-value primary-color">{{ supportPhone }}</text>-->
+<!--        </view>-->
+<!--      </view>-->
+    </view>
+
+    <view class="footer">
+      <button class="logout-button" @click="logout">退出登录</button>
+    </view>
+    <CustomTabbar :current="2"/>
+  </view>
+</template>
+
+<script>
+import CustomTabbar from '@/components/cjx/tabbar_hexiao_jxs.vue';
+import {clearAdminToken, getAdminInfo} from "../../../../utils/auth";
+import {getAdminUserInfo} from "../../../../api/hexiao";
+
+export default {
+  components: {
+    CustomTabbar
+  },
+  data() {
+    return {
+      imgurl:"https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu",
+      // 用户信息 - 通常从登录后的storage或API获取
+      userInfo: {
+        avatarUrl: 'https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/user-info.png', // 默认头像
+        phone: '',
+        role: '',
+        points: 0,
+        shopCount: 0
+      },
+      appVersion: '1.0',
+      supportPhone: '40012547856'
+    };
+  },
+  onLoad(){
+    let info = getAdminInfo();
+    this.userInfo.phone = info.phone;
+    this.userInfo.role = info.roleName;
+  },
+  onShow(){
+    getAdminUserInfo().then(res=>{
+      this.userInfo.shopCount = res.data.storeCount;
+      this.userInfo.points = res.data.pointTotal;
+    })
+  },
+  methods: {
+    // 格式化数字,添加逗号
+    formatNumber(num) {
+      if (num === undefined || num === null) return '';
+      return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+    },
+
+    // 列表项点击事件
+    navigateToAction(action) {
+      switch(action) {
+        case 'points':
+          console.log('跳转到我的积分页面');
+          uni.navigateTo({ url: '/pages/points/points' }); // 请替换为您的积分页面路径
+          break;
+        case 'shops':
+          console.log('跳转到我的店铺页面');
+          uni.navigateTo({ url: '/pages/shops/shops' }); // 请替换为您的店铺页面路径
+          break;
+      }
+    },
+
+    // 拨打客服电话
+    callSupport() {
+      uni.makePhoneCall({
+        phoneNumber: this.supportPhone,
+        success: () => {
+          console.log('拨打电话成功');
+        },
+        fail: (err) => {
+          console.log('拨打电话失败', err);
+          uni.showModal({
+            title: '拨号失败',
+            content: `无法拨打电话,请手动拨打客服热线:${this.supportPhone}`,
+            showCancel: false
+          })
+        }
+      });
+    },
+
+    // 退出登录
+    logout() {
+      uni.showModal({
+        title: '提示',
+        content: '您确定要退出登录吗?',
+        success: (res) => {
+          if (res.confirm) {
+            console.log('用户点击确定,执行退出登录操作');
+            clearAdminToken();
+            // 跳转到登录页
+            uni.reLaunch({
+              url: '/pages/cjx/hexiao/login' // 请替换为您的登录页路径
+            });
+          }
+        }
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+// 页面整体背景
+.page-container {
+  background-color: #f5f6fa;
+  min-height: 100vh;
+  position: relative;
+  box-sizing: border-box; /* 确保 padding 不会增加总高度 */
+
+  /* --- 关键改动 2 --- */
+  /* 增加底部内边距,为“退出登录”按钮和自定义tabBar都留出空间 */
+  &.with-padding-bottom {
+    padding-bottom: 280rpx;
+  }
+}
+
+// 顶部用户信息区
+.profile-header {
+  background: linear-gradient(135deg, #6ca1ff, #87d7dc);
+  height: 400rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding-bottom: 80rpx; // 为下方卡片留出重叠空间
+}
+
+.avatar-wrapper {
+  position: relative;
+
+  .avatar {
+    width: 150rpx;
+    height: 150rpx;
+    border-radius: 50%;
+    border: 4rpx solid rgba(255, 255, 255, 0.5);
+  }
+
+  .badge {
+    position: absolute;
+    bottom: 0;
+    right: -10rpx;
+    background-color: #ff9900;
+    color: #ffffff;
+    font-size: 22rpx;
+    padding: 4rpx 12rpx;
+    border-radius: 20rpx;
+    border: 2rpx solid #ffffff;
+  }
+}
+
+.phone-number {
+  color: #ffffff;
+  font-size: 36rpx;
+  font-weight: bold;
+  margin-top: 20rpx;
+  letter-spacing: 1rpx;
+}
+.item-icon {
+  width: 44rpx;
+  height: 44rpx;
+  margin-right: 15rpx; /* 图标和文字的间距 */
+}
+// 内容卡片
+.content-card {
+  background-color: #ffffff;
+  margin: -80rpx 30rpx 0 30rpx; // 负margin实现向上重叠效果
+  border-radius: 20rpx;
+  padding: 20rpx 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+  position: relative; // 确保在header之上
+  z-index: 10;
+}
+
+.list-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 35rpx 0;
+  border-bottom: 1rpx solid #f5f5f5;
+
+  &:last-child {
+    border-bottom: none;
+  }
+
+  .item-left {
+    display: flex;
+    align-items: center;
+
+    .item-label {
+      margin-left: 25rpx;
+      font-size: 30rpx;
+      color: #333;
+    }
+  }
+
+  .item-right {
+    display: flex;
+    align-items: center;
+
+    .item-value {
+      font-size: 28rpx;
+      color: #999;
+      margin-right: 10rpx;
+    }
+
+    .primary-color {
+      color: #6ca1ff; // 客服电话用主题色突出
+      font-weight: 500;
+    }
+  }
+}
+
+// 底部
+.footer {
+  position: fixed;
+  /* bottom值 = 自定义tabBar的高度(100rpx) + iPhone等机型的底部安全区 */
+  bottom: calc(100rpx + constant(safe-area-inset-bottom));
+  bottom: calc(100rpx + env(safe-area-inset-bottom));
+
+  left: 0;
+  width: 100%;
+  padding: 30rpx;
+  box-sizing: border-box;
+  background-color: #f5f6fa; // 设置背景色防止透明
+  z-index: 90; // 比 tabBar 低,比页面内容高
+}
+
+.logout-button {
+  background-color: #ffffff;
+  color: #555555;
+  border-radius: 50rpx;
+  font-weight: normal;
+  font-size: 32rpx;
+  border: 1rpx solid #e0e0e0;
+  &::after {
+    border: none;
+  }
+}
+</style>

+ 315 - 0
pages/cjx/hexiao/jxs/ywy_list.vue

@@ -0,0 +1,315 @@
+<template>
+  <view class="page-container">
+    <view class="search-wrapper">
+      <view class="search-bar">
+        <uni-icons type="search" size="20" color="#999"></uni-icons>
+        <input
+            class="search-input"
+            v-model="searchQuery"
+            placeholder="输入业务员姓名/账号搜索"
+            placeholder-class="placeholder"
+            confirm-type="search"
+            @confirm="handleSearch"
+        />
+      </view>
+    </view>
+
+    <scroll-view class="list-container" scroll-y="true" @scrolltolower="loadMore">
+      <view v-if="salespersonList.length === 0 && loadStatus !== 'loading'" class="empty-list">
+        <text>暂无业务员数据</text>
+      </view>
+
+      <view v-for="item in salespersonList" :key="item.id" class="salesperson-card">
+        <view class="card-header">
+          <view class="header-left">
+            <uni-icons type="person-filled" size="22" color="#3c82f8"></uni-icons>
+            <text class="salesperson-name">{{ item.nick_name }}</text>
+          </view>
+          <view class="status-badge" :class="item.status_ === 1 ? 'active' : 'inactive'">
+            {{ item.status_ === 1 ? '在职' : '离职' }}
+          </view>
+        </view>
+
+        <view class="card-body">
+          <view class="detail-row" @click="copy(item.account)">
+            <text class="detail-label">业务员账号</text>
+            <text class="detail-value">{{ item.tel_ }}</text>
+          </view>
+          <view class="detail-row clickable" @click="goToStores(item.id)">
+            <text class="detail-label">门店数量</text>
+            <view class="detail-value with-arrow">
+              <text>{{ item.storeCount }}家</text>
+              <uni-icons type="right" size="14" color="#999"></uni-icons>
+            </view>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">创建时间</text>
+            <text class="detail-value">{{ item.create_time }}</text>
+          </view>
+          <view class="detail-row clickable" @click="viewSalesData(item.id)">
+            <text class="detail-label">销售数据</text>
+            <view class="detail-value with-arrow">
+              <text class="view-text">查看</text>
+              <uni-icons type="right" size="14" color="#999"></uni-icons>
+            </view>
+          </view>
+        </view>
+
+        <view class="card-footer">
+          <view class="action-btn" @click="editSalesperson(item.id)">
+            <uni-icons type="compose" size="18" color="#3c82f8"></uni-icons>
+            <text>编辑</text>
+          </view>
+          <view class="action-btn" @click="viewPatrolRecords(item.id)">
+            <uni-icons type="home-filled" size="18" color="#3c82f8"></uni-icons>
+            <text>巡店记录</text>
+          </view>
+          <view class="action-btn delete-btn" @click="deleteSalesperson(item.id)">
+            <uni-icons type="trash-filled" size="18" color="#e54d42"></uni-icons>
+            <text>删除</text>
+          </view>
+        </view>
+      </view>
+
+      <uni-load-more :status="loadStatus"></uni-load-more>
+    </scroll-view>
+
+    <view class="fixed-footer">
+      <button class="add-btn" @click="addNewSalesperson">新增业务员</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import {ywyList,delYwy} from "../../../../api/hexiao";
+
+export default {
+  data() {
+    return {
+      searchQuery: '',
+      salespersonList: [],
+      pagination: { page: 1, limit: 10 },
+      loadStatus: 'more',
+      isLoading: false,
+    };
+  },
+  onLoad() {
+    this.fetchSalespeople(true);
+  },
+  onShow(){
+    this.fetchSalespeople(true);
+  },
+  onPullDownRefresh() {
+    // this.fetchSalespeople(true);
+  },
+  methods: {
+    build(){
+      uni.showToast({
+        title: '功能建设中',
+        icon: 'none'
+      });
+    },
+    fetchSalespeople(isRefresh = false) {
+      if (this.isLoading || (this.loadStatus === 'noMore' && !isRefresh)) {
+        return;
+      }
+      if (isRefresh) {
+        this.pagination.page = 1;
+        this.salespersonList = [];
+        this.loadStatus = 'more';
+      }
+      this.isLoading = true;
+      this.loadStatus = 'loading';
+
+      // --- 模拟API请求 ---
+      console.log(`请求第 ${this.pagination.page} 页...`);
+
+      ywyList(this.searchQuery).then(res=>{
+        let mockData = res.data;
+        this.salespersonList = mockData
+        // uni.stopPullDownRefresh();
+        // if (mockData.length > 0 && !noMoreData) {
+        //   this.salespersonList = [...this.salespersonList, ...mockData];
+        //   this.pagination.page++;
+        //   this.loadStatus = 'more';
+        // } else {
+        //   this.loadStatus = 'noMore';
+        // }
+        this.isLoading = false;
+        this.loadStatus = 'noMore';
+      })
+
+
+    },
+    loadMore() {
+      this.fetchSalespeople();
+    },
+    handleSearch() {
+      // uni.showToast({ title: '触发搜索', icon: 'none' });
+      this.fetchSalespeople(true);
+    },
+    copy(text) {
+      uni.setClipboardData({
+        data: text,
+        success: () => uni.showToast({ title: '已复制' })
+      });
+    },
+    goToStores(id) { console.log('查看门店列表 ID:', id); this.build()},
+    viewSalesData(id) { console.log('查看销售数据 ID:', id); this.build() },
+    editSalesperson(id) { console.log('编辑业务员 ID:', id); },
+    viewPatrolRecords(id) { console.log('查看巡店记录 ID:', id); this.build()},
+    deleteSalesperson(id) {
+      let self = this;
+      uni.showModal({
+        title: '确认删除',
+        content: '您确定要删除该业务员吗?',
+        confirmColor: '#e54d42',
+        success: (res) => {
+          if (res.confirm) {
+
+            delYwy(id).then(res=>{
+              self.fetchSalespeople(true);
+            })
+          }
+        }
+      });
+    },
+    addNewSalesperson() {
+      console.log('跳转到新增业务员页面');
+      uni.navigateTo({ url: '/pages/cjx/hexiao/jxs/add_ywy' });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f6fa;
+}
+
+.search-wrapper {
+  padding: 20rpx;
+  background-color: #ffffff;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+.search-bar {
+  display: flex;
+  align-items: center;
+  background-color: #f5f6fa;
+  border-radius: 50rpx;
+  padding: 0 25rpx;
+  height: 70rpx;
+}
+.search-input { flex: 1; font-size: 28rpx; margin-left: 15rpx; }
+.placeholder { color: #b0b0b0; }
+
+.list-container {
+  flex: 1;
+  height: 100%;
+  padding-bottom: 160rpx; // 为底部按钮留出空间
+}
+.empty-list { text-align: center; color: #999; padding-top: 150rpx; }
+
+.salesperson-card {
+  background-color: #ffffff;
+  border-radius: 16rpx;
+  margin: 20rpx;
+  padding: 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  .header-left {
+    display: flex;
+    align-items: center;
+  }
+  .salesperson-name {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #333;
+    margin-left: 15rpx;
+  }
+}
+
+.status-badge {
+  font-size: 24rpx;
+  padding: 6rpx 16rpx;
+  border-radius: 8rpx;
+  color: #fff;
+  &.active {
+    background-color: #3c82f8;
+  }
+  &.inactive {
+    background-color: #c0c4cc;
+  }
+}
+
+.card-body {
+  padding: 20rpx 0;
+}
+
+.detail-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15rpx 0;
+  font-size: 28rpx;
+  .detail-label { color: #666; }
+  .detail-value { color: #333; }
+  .with-arrow {
+    display: flex;
+    align-items: center;
+    color: #999;
+    .view-text {
+      color: #3c82f8;
+    }
+  }
+  &.clickable:active {
+    background-color: #f9f9f9;
+  }
+}
+
+.card-footer {
+  display: flex;
+  justify-content: space-around;
+  border-top: 1rpx solid #f5f5f5;
+  margin-top: 10rpx;
+  padding-top: 25rpx;
+}
+.action-btn {
+  display: flex;
+  align-items: center;
+  font-size: 26rpx;
+  color: #3c82f8;
+  text { margin-left: 8rpx; }
+  &.delete-btn { color: #e54d42; }
+}
+
+.fixed-footer {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  background-color: #f5f6fa;
+  padding: 20rpx 30rpx;
+  padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+  box-sizing: border-box;
+  z-index: 100;
+}
+.add-btn {
+  background-color: #3c82f8;
+  color: white;
+  border-radius: 50rpx;
+  font-size: 32rpx;
+  height: 90rpx;
+  line-height: 90rpx;
+  &::after { border: none; }
+}
+</style>

+ 341 - 0
pages/cjx/hexiao/login.vue

@@ -0,0 +1,341 @@
+<template>
+  <view class="page-container">
+    <view class="welcome-title">
+      超吉炫,欢迎您!
+    </view>
+
+    <view class="main-card">
+      <image class="logo" :src="imgurl+'/cjx/hexiao/lobin_lobin.png'" mode="aspectFit"></image>
+
+      <view class="user-type-selector">
+        <view
+            class="type-button"
+            :class="{ 'active': userType === 'distributor' }"
+            @click="switchUserType('distributor')"
+        >
+          <uni-icons type="person" :color="userType === 'distributor' ? '#fff' : '#4a8af4'" size="20"></uni-icons>
+          <text>经销商</text>
+        </view>
+        <view
+            class="type-button"
+            :class="{ 'active': userType === 'salesman' }"
+            @click="switchUserType('salesman')"
+        >
+          <uni-icons type="staff" :color="userType === 'salesman' ? '#fff' : '#4a8af4'" size="20"></uni-icons>
+          <text>业务员</text>
+        </view>
+      </view>
+
+      <view class="form-group">
+        <view class="input-wrapper">
+          <uni-icons type="person" size="24" color="#cccccc"></uni-icons>
+          <input class="input-field" type="number" v-model="phone" placeholder="请输入手机号" placeholder-class="placeholder" />
+        </view>
+
+        <view class="input-wrapper">
+          <uni-icons type="locked" size="24" color="#cccccc"></uni-icons>
+          <input class="input-field" type="number" v-model="code" placeholder="请输入验证码" placeholder-class="placeholder" />
+          <text
+              class="code-btn"
+              :class="{ 'disabled': isCountingDown }"
+              @click="getVerificationCode"
+          >
+            {{ codeBtnText }}
+          </text>
+        </view>
+      </view>
+
+      <button class="login-btn" @click="handleLogin">登 录</button>
+
+<!--      <view class="version-info">-->
+<!--        版本号:V1.0.0-->
+<!--      </view>-->
+    </view>
+  </view>
+</template>
+
+<script>
+// 1. 导入你的API方法
+import { getVerificationCode, login } from '@/api/hexiao.js';
+import {getAdminToken, setAdminInfo, setAdminToken} from "../../../utils/auth";
+
+export default {
+  data() {
+    return {
+      userType: 'distributor',
+      phone: '18569549881',
+      code: '888888',
+      codeBtnText: '获取手机验证码',
+      isCountingDown: false,
+      countdown: 60,
+      timer: null,
+      imgurl:"https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu",
+
+    }
+  },
+  onUnload() {
+    if (this.timer) {
+      clearInterval(this.timer);
+      this.timer = null;
+    }
+  },
+  onLoad(){
+    let token = getAdminToken()
+    if(token){
+      this.doLoginSuccess();
+    }
+  },
+  methods: {
+    doLoginSuccess(){
+      let userType = 0;
+      let pageUrl = '/pages/cjx/hexiao/ywy/index';
+      if(this.userType === 'distributor'){
+        pageUrl = '/pages/cjx/hexiao/jxs/index';
+      }
+      if(this.userType === 'salesman'){
+         pageUrl = '/pages/cjx/hexiao/ywy/index';
+      }
+      uni.reLaunch({
+        url: pageUrl
+      });
+    },
+    switchUserType(type) {
+      this.userType = type;
+    },
+
+    // --- 更新:调用获取验证码的API ---
+    getVerificationCode() {
+      if (this.isCountingDown) {
+        return;
+      }
+      if(!this.phone) {
+        uni.showToast({ title: '请输入手机号', icon: 'none' });
+        return;
+      }
+      if(this.timer){
+        clearInterval(this.timer);
+        this.timer = null;
+      }
+
+      let userType = 0;
+      if(this.userType === 'distributor'){
+        userType = 1;
+      }
+      if(this.userType === 'salesman'){
+        userType = 2;
+      }
+
+      // 调用API
+      getVerificationCode(this.phone,userType).then(res => {
+        if(res.code !== 0){
+          uni.showToast({ title: res.msg || '找不到用户信息,请确认', icon: 'none' });
+          return;
+        }
+        console.log('验证码接口成功返回:', res);
+        uni.showToast({ title: res.msg || '验证码已发送', icon: 'success' });
+
+        // 开始倒计时
+        this.isCountingDown = true;
+        this.codeBtnText = `${this.countdown}s后重新获取`;
+        this.timer = setInterval(() => {
+          this.countdown--;
+          if (this.countdown > 0) {
+            this.codeBtnText = `${this.countdown}s后重新获取`;
+          } else {
+            clearInterval(this.timer);
+            this.timer = null;
+            this.countdown = 60;
+            this.isCountingDown = false;
+            this.codeBtnText = '获取手机验证码';
+          }
+        }, 1000);
+
+      }).catch(err => {
+        // if(res.code === -99){
+        //   uni.showToast({ title: err.msg || '找不到用户信息,请确认', icon: 'none' });
+        //   return;
+        // }
+        // console.error('验证码接口失败返回:', err);
+        // uni.showToast({ title: err.msg || '发送失败,请重试', icon: 'none' });
+      });
+    },
+    setUserInfo(res){
+      setAdminInfo(res);
+    },
+    // --- 更新:调用登录的API ---
+    handleLogin() {
+      if(!this.phone || !this.code) {
+        uni.showToast({ title: '请填写完整信息', icon: 'none' });
+        return;
+      }
+
+      uni.showLoading({ title: '登录中...' });
+      let userType = 0;
+      if(this.userType === 'distributor'){
+        userType = 1;
+      }
+      if(this.userType === 'salesman'){
+        userType = 2;
+      }
+      uni.login({
+        provider: 'weixin', //使用微信登录
+        success: function (loginRes) {
+          let jsCode = loginRes.code;
+          login(this.phone, this.code,userType,jsCode).then(res => {
+            // 登录成功 (res.code === 0)
+            uni.hideLoading();
+
+            if (res.code === 0) {
+              if (res.data.isWxAuth == 1) {
+                uni.setStorageSync('Authorization-status', true);
+              }
+              setAdminToken(res.data.sessionId)
+              this.setUserInfo(res.data);
+              this.doLoginSuccess();
+            } else {
+              console.log('登录失败!' + res.msg);
+              uni.showToast({ title: err.msg || '登录失败', icon: 'none' });
+            }
+          }).catch(err => {
+            // 登录失败 (res.code !== 0 或网络错误)
+            uni.hideLoading();
+            console.error('登录失败:', err);
+            uni.showToast({ title: err.msg || '登录失败', icon: 'none' });
+          });
+        }
+      });
+      // 调用API
+
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.page-container {
+  width: 100vw;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  overflow: hidden;
+  min-height: 100vh;
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/login_bg.png");
+  background-size: contain;
+  background-position: center;
+  background-repeat: no-repeat;
+  position: relative;
+}
+
+.welcome-title {
+  font-size: 48rpx;
+  font-weight: bold;
+  color: #488CFF;
+  margin-top: 28%;
+  margin-bottom: 40rpx;
+}
+
+.main-card {
+  width: 88%;
+  border-radius: 40rpx;
+  padding: 40rpx;
+  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  position: relative;
+  top: 10%;
+  height: 100%;
+}
+
+.logo {
+  width: 150rpx;
+  height: 150rpx;
+  background-color: #f0f0f0;
+  margin-bottom: 40rpx;
+  border-radius: 20rpx;
+}
+
+.user-type-selector {
+  display: flex;
+  justify-content: center;
+  width: 100%;
+  margin-bottom: 50rpx;
+
+  .type-button {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 30rpx;
+    padding: 15rpx 40rpx;
+    border-radius: 50rpx;
+    transition: all 0.3s ease;
+    border: 1px solid #4a8af4;
+    color: #4a8af4;
+    background-color: #fff;
+    margin: 0 15rpx;
+
+    .uni-icons {
+      margin-right: 10rpx;
+    }
+
+    &.active {
+      background-color: #4a8af4;
+      color: #ffffff;
+      border-color: #4a8af4;
+    }
+  }
+}
+
+.form-group {
+  width: 100%;
+}
+
+.input-wrapper {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  padding: 20rpx 0;
+  border-bottom: 1px solid #f0f0f0;
+  margin-bottom: 30rpx;
+
+  .input-field {
+    flex: 1;
+    font-size: 30rpx;
+    margin-left: 20rpx;
+  }
+
+  .placeholder {
+    color: #cccccc;
+  }
+
+  .code-btn {
+    font-size: 28rpx;
+    color: #4a8af4;
+    white-space: nowrap;
+    padding-left: 20rpx;
+    transition: color 0.3s; // 添加颜色过渡效果
+
+    // --- 新增:置灰样式 ---
+    &.disabled {
+      color: #cccccc;
+    }
+  }
+}
+
+.login-btn {
+  width: 100%;
+  background: #4a8af4;
+  color: #ffffff;
+  font-size: 32rpx;
+  border-radius: 50rpx;
+  margin-top: 40rpx;
+  box-shadow: 0 10rpx 20rpx rgba(74, 138, 244, 0.3);
+}
+
+.version-info {
+  margin-top: 40rpx;
+  font-size: 24rpx;
+  color: #b0b0b0;
+}
+</style>

+ 249 - 0
pages/cjx/hexiao/ywy/add_goods_record.vue

@@ -0,0 +1,249 @@
+<template>
+  <view class="page-container">
+    <view class="search-wrapper">
+      <view class="search-bar">
+        <uni-icons type="search" size="20" color="#999"></uni-icons>
+        <input
+            class="search-input"
+            v-model="searchQuery"
+            placeholder="输入门店名称/上货日期搜索"
+            placeholder-class="placeholder"
+            confirm-type="search"
+            @confirm="handleSearch"
+        />
+      </view>
+    </view>
+
+    <scroll-view class="list-container" scroll-y="true" @scrolltolower="loadMore">
+      <view v-if="recordList.length === 0 && loadStatus !== 'loading'" class="empty-list">
+        <text>暂无上货记录</text>
+      </view>
+
+      <view v-for="record in recordList" :key="record.id" class="record-card">
+        <view class="card-decorator"></view>
+        <view class="card-content">
+          <view class="card-header">
+            <uni-icons type="calendar-filled" size="20" color="#3c82f8"></uni-icons>
+            <text class="record-id">{{ record.id }}</text>
+          </view>
+
+          <view class="details-section">
+            <view class="detail-row">
+              <text class="detail-label">上货商品总数</text>
+              <text class="detail-value">{{ record.totalNum }}</text>
+            </view>
+
+            <view class="detail-row product-item" v-for="(product, index) in record.products" :key="index">
+              <text class="detail-label product-name">{{ product.name }}</text>
+              <text class="detail-value">{{ product.quantity }}{{ product.unit }}</text>
+            </view>
+
+            <view class="detail-row">
+              <text class="detail-label">上货门店</text>
+              <text class="detail-value">{{ record.storeName }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">上货时间</text>
+              <text class="detail-value">{{ record.activeTime }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">上货奖励</text>
+              <text class="detail-value reward">{{ record.reward==null?"0":record.reward }}积分</text>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <uni-load-more :status="loadStatus"></uni-load-more>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import {queryActiveRecord} from "../../../../api/hexiao";
+
+export default {
+  data() {
+    return {
+      searchQuery: '',
+      recordList: [],
+      pagination: {
+        page: 1,
+        limit: 10,
+      },
+      loadStatus: 'more', // 'more'-加载前, 'loading'-加载中, 'noMore'-没有更多了
+      isLoading: false,
+    };
+  },
+  onLoad() {
+    // 页面加载时获取第一页数据
+    this.fetchRecords(true);
+  },
+  // 监听下拉刷新
+  onPullDownRefresh() {
+    this.fetchRecords(true);
+  },
+  methods: {
+    // 核心:获取记录列表数据
+    async fetchRecords(isRefresh = false) {
+      if (this.isLoading || (this.loadStatus === 'noMore' && !isRefresh)) {
+        return;
+      }
+
+      if (isRefresh) {
+        this.pagination.page = 1;
+        this.recordList = [];
+        this.loadStatus = 'more';
+      }
+
+      this.isLoading = true;
+      this.loadStatus = 'loading';
+
+      // 在这里替换成您真实的 uni.request API 调用
+      console.log(`正在请求第 ${this.pagination.page} 页数据...`);
+
+      queryActiveRecord(this.pagination.page,this.pagination.limit).then(res=>{
+        let mockData = res.data.records;
+        if (mockData.length > 0) {
+          this.recordList = [...this.recordList, ...mockData];
+          this.pagination.page++;
+          this.loadStatus = 'more';
+        } else {
+          this.loadStatus = 'noMore';
+        }
+        this.isLoading = false;
+        uni.stopPullDownRefresh(); // 停止下拉刷新动画
+      });
+
+
+      // --- [模拟API请求结束] ---
+
+
+
+
+    },
+
+    // 上拉加载更多
+    loadMore() {
+      this.fetchRecords();
+    },
+
+    // 搜索
+    handleSearch() {
+      // 在实际项目中,搜索应该调用API,这里为简单起见只做前端筛选
+      // this.fetchRecords(true);
+      uni.showToast({ title: '触发搜索', icon: 'none' });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f6fa;
+}
+
+.search-wrapper {
+  padding: 20rpx;
+  background-color: #ffffff;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+
+.search-bar {
+  display: flex;
+  align-items: center;
+  background-color: #f5f6fa;
+  border-radius: 50rpx;
+  padding: 0 25rpx;
+  height: 70rpx;
+}
+.search-input {
+  flex: 1;
+  font-size: 28rpx;
+  margin-left: 15rpx;
+}
+.placeholder {
+  color: #b0b0b0;
+}
+
+.list-container {
+  flex: 1;
+  height: 100%; // 必须给scroll-view一个明确的高度
+}
+
+.empty-list {
+  text-align: center;
+  color: #999;
+  padding-top: 150rpx;
+}
+
+.record-card {
+  background-color: #ffffff;
+  border-radius: 16rpx;
+  margin: 20rpx;
+  position: relative;
+  overflow: hidden;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+}
+
+.card-decorator {
+  position: absolute;
+  left: 0;
+  top: 0;
+  bottom: 0;
+  width: 8rpx;
+  background-color: #e3efff;
+}
+
+.card-content {
+  padding: 30rpx;
+  padding-left: 40rpx;
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  padding-bottom: 20rpx;
+  border-bottom: 1rpx solid #f5f5f5;
+
+  .record-id {
+    font-size: 30rpx;
+    font-weight: bold;
+    color: #333;
+    margin-left: 15rpx;
+  }
+}
+
+.details-section {
+  padding-top: 10rpx;
+}
+
+.detail-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15rpx 0;
+  font-size: 28rpx;
+
+  .detail-label {
+    color: #666;
+  }
+
+  .detail-value {
+    color: #333;
+  }
+
+  &.product-item .detail-label {
+    color: #333; // 商品名称颜色深一些
+    font-weight: 400;
+  }
+
+  .reward {
+    color: #ff9900;
+    font-weight: 500;
+  }
+}
+</style>

+ 230 - 0
pages/cjx/hexiao/ywy/add_retail.vue

@@ -0,0 +1,230 @@
+<template>
+  <view class="page-container">
+    <view class="form-card">
+
+      <view class="form-item">
+        <text class="form-label">店铺名称</text>
+        <input
+            class="form-input"
+            v-model="formData.storeName"
+            placeholder="请输入店铺名称"
+            placeholder-class="placeholder"
+        />
+      </view>
+
+      <view class="form-item">
+        <text class="form-label">店主名称</text>
+        <input
+            class="form-input"
+            v-model="formData.ownerName"
+            placeholder="请输入店主名称"
+            placeholder-class="placeholder"
+        />
+      </view>
+
+      <view class="form-item">
+        <text class="form-label">联系方式</text>
+        <input
+            class="form-input"
+            type="number"
+            maxlength="11"
+            v-model="formData.contact"
+            placeholder="请输入联系方式"
+            placeholder-class="placeholder"
+        />
+      </view>
+
+      <view class="form-item">
+        <text class="form-label">店铺地址</text>
+        <input
+            class="form-input"
+            v-model="formData.address"
+            placeholder="请输入店铺地址"
+            placeholder-class="placeholder"
+        />
+      </view>
+
+    </view>
+
+    <view class="footer-save-button" v-if="edit">
+      <button class="save-btn" @click="submitForm" >保 存</button>
+    </view>
+
+  </view>
+</template>
+
+<script>
+import {addStore, getRetailDetail, updateStore} from "../../../../api/hexiao";
+
+export default {
+  data() {
+    return {
+      edit:false,
+      // 表单数据
+      formData: {
+        storeId: 0,
+        storeName: '',
+        ownerName: '',
+        contact: '',
+        address: ''
+      }
+    };
+  },
+  onLoad(opt){
+    this.edit = opt.edit == 1;
+      if(opt.id){
+        this.formData.storeId = opt.id;
+        getRetailDetail(opt.id).then(res=>{
+          let data = res.data;
+          this.formData = {
+            storeId: data.id,
+            storeName: data.store_name,
+            ownerName: data.contact_name,
+            contact: data.contact_phone,
+            address: data.address
+          }
+        })
+      }
+  },
+  methods: {
+    // 提交表单
+    submitForm() {
+      // --- 手动进行数据校验 ---
+      if (!this.formData.storeName) {
+        uni.showToast({ title: '请输入店铺名称', icon: 'none' });
+        return;
+      }
+      if (!this.formData.ownerName) {
+        uni.showToast({ title: '请输入店主名称', icon: 'none' });
+        return;
+      }
+      if (!this.formData.contact) {
+        uni.showToast({ title: '请输入联系方式', icon: 'none' });
+        return;
+      }
+      // 简单的手机号格式校验
+      if (!/^1[3-9]\d{9}$/.test(this.formData.contact)) {
+        uni.showToast({ title: '请输入正确的手机号码', icon: 'none' });
+        return;
+      }
+      if (!this.formData.address) {
+        uni.showToast({ title: '请输入店铺地址', icon: 'none' });
+        return;
+      }
+
+      console.log('校验通过,准备提交的数据:', this.formData);
+
+      // 执行提交逻辑
+      uni.showLoading({ title: '正在保存...' });
+      if(this.formData.storeId>0){
+        updateStore(this.formData.storeId,this.formData.storeName,this.formData.ownerName,this.formData.contact,this.formData.address)
+            .then(res=>{
+              uni.hideLoading();
+              if(res.code == 0){
+                uni.showToast({
+                  title: '保存成功',
+                  icon: 'success'
+                });
+                uni.navigateBack();
+              }else{
+                uni.showToast({
+                  title: res.msg,
+                  icon: 'none'
+                });
+              }
+
+
+            })
+      }else{
+        addStore(this.formData.storeName,this.formData.ownerName,this.formData.contact,this.formData.address)
+            .then(res=>{
+              uni.hideLoading();
+              if(res.code == 0){
+                uni.showToast({
+                  title: '保存成功',
+                  icon: 'success'
+                });
+                uni.navigateBack();
+              }else{
+                uni.showToast({
+                  title: res.msg,
+                  icon: 'none'
+                });
+              }
+
+
+            })
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+  min-height: 100vh;
+  background: linear-gradient(to bottom, #e4efff, #f5f6fa 40%);
+  padding: 30rpx;
+  box-sizing: border-box;
+}
+
+.form-card {
+  background-color: #ffffff;
+  border-radius: 20rpx;
+  padding: 0 40rpx; // 左右内边距
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+}
+
+.form-item {
+  padding: 30rpx 0;
+  border-bottom: 1rpx solid #f0f0f0;
+
+  // 最后一个item不需要下边框
+  &:last-child {
+    border-bottom: none;
+  }
+}
+
+.form-label {
+  display: block; // 确保独占一行
+  font-size: 30rpx;
+  color: #333;
+  font-weight: 500;
+}
+
+.form-input {
+  margin-top: 20rpx; // 与label的间距
+  font-size: 28rpx;
+  color: #333;
+}
+
+.placeholder {
+  color: #c0c4cc;
+}
+
+.footer-save-button {
+  position: fixed;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  padding: 20rpx 30rpx;
+  background-color: #f5f6fa;
+  box-sizing: border-box;
+
+  padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+}
+
+.save-btn {
+  background-color: #409eff;
+  color: #ffffff;
+  border-radius: 50rpx;
+  font-size: 32rpx;
+  height: 90rpx;
+  line-height: 90rpx;
+
+  &::after {
+    border: none;
+  }
+}
+</style>

+ 321 - 0
pages/cjx/hexiao/ywy/index.vue

@@ -0,0 +1,321 @@
+<template>
+  <view class="page-container">
+    <view class="page-title">
+      首页
+    </view>
+    <view class="header-section">
+      <view class="greeting-text">{{ userInfo.role }} {{ userInfo.name }}</view>
+      <view class="title-text">提供的服务</view>
+    </view>
+
+    <view class="content-grid">
+      <view class="card large-card" @click="scanCode('stock')">
+        <view class="large-card-text">
+          <view class="card-title" style="font-size: 40rpx">扫码上货</view>
+          <view class="card-subtitle">一键扫码上货</view>
+          <view class="scan-button">去扫码</view>
+        </view>
+        <view class="large-card-icon">
+          <image :src="imgurl+'/cjx/hexiao/shanghuo.png'" mode="aspectFit"></image>
+        </view>
+      </view>
+
+      <view class="card" @click="scanCode('verify')">
+        <view class="icon-wrapper" style="background-color: #e4f7ed;">
+          <image :src="imgurl+'/cjx/hexiao/hexiao.png'" mode="aspectFit"></image>
+        </view>
+        <view class="card-title">扫码核销</view>
+      </view>
+
+      <view class="card" @click="navigateTo('patrolRecords')">
+        <view class="icon-wrapper" style="background-color: #f2e7ff;">
+          <image :src="imgurl+'/cjx/hexiao/xundian.png'" mode="aspectFit"></image>
+        </view>
+        <view class="card-title">巡店记录</view>
+      </view>
+
+      <view class="card small-card" @click="navigateTo('stockRecords')">
+        <view class="icon-wrapper" style="background-color: #fff8e1;">
+          <image :src="imgurl+'/cjx/hexiao/hexiao.png'" mode="aspectFit"></image>
+        </view>
+        <text class="card-title">上货记录</text>
+      </view>
+
+      <view class="card small-card" @click="navigateTo('verifyRecords')">
+        <view class="icon-wrapper" style="background-color: #ffede6;">
+          <image :src="imgurl+'/cjx/hexiao/hxjilu.png'" mode="aspectFit"></image>
+        </view>
+        <text class="card-title">核销记录</text>
+      </view>
+
+      <view class="card stat-card mendian-card" @click="navigateTo('storeStats')">
+        <view class="stat-content">
+          <view class="stat-title">门店统计</view>
+          <view class="stat-value">{{ stats.storeCount }}</view>
+          <view class="stat-label">总数</view>
+        </view>
+      </view>
+
+      <view class="card stat-card jifen-card" @click="navigateTo('pointsStats')">
+        <view class="stat-content">
+          <view class="stat-title">积分统计</view>
+          <view class="stat-value">{{ formatNumber(stats.pointsCount) }}</view>
+          <view class="stat-label">总数</view>
+        </view>
+      </view>
+    </view>
+    <select-store-drawer
+        :show="showDrawer"
+        :selected-id="selectedStore ? selectedStore.id : null"
+        @close="showDrawer = false"
+        @select="onStoreSelect"
+    ></select-store-drawer>
+    <CustomTabbar :current="0"/>
+  </view>
+
+</template>
+
+<script>
+import CustomTabbar from '@/components/cjx/tabbar_hexiao_ywy.vue';
+import {getAdminInfo} from "../../../../utils/auth";
+import {getAdminUserInfo} from "../../../../api/hexiao";
+import SelectStoreDrawer from '@/components/cjx/select-store-drawer.vue';
+
+export default {
+  components: {
+    CustomTabbar,
+    SelectStoreDrawer
+  },
+  data() {
+    return {
+      selectedStore: 0,
+      showDrawer:false,
+      imgurl:"https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu",
+      userInfo: {
+        name: '',
+        role: '超吉炫'
+      },
+      stats: {
+        storeCount: 12,
+        pointsCount: 1120150
+      }
+    };
+  },
+  onLoad(){
+    let info = getAdminInfo();
+    this.userInfo.role = this.userInfo.role+""+info.roleName;
+    this.userInfo.name = info.userName;
+  },
+  onShow(){
+    getAdminUserInfo().then(res=>{
+      this.stats.storeCount = res.data.storeCount;
+      this.stats.pointsCount = res.data.pointTotal;
+    })
+  },
+  methods: {
+    // 监听从抽屉组件传来的 select 事件
+
+    onStoreSelect(store) {
+      console.log('在首页接收到选中的门店:', store);
+      this.selectedStore = store;
+      uni.navigateTo({ url: '/pages/cjx/hexiao/ywy/scan_code?storeId='+store.id });
+    },
+    // 格式化数字,添加千位分隔符
+    formatNumber(num) {
+      return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+    },
+    // 扫码事件
+    scanCode(type) {
+      // uni.navigateTo({ url: `/pages/cjx/hexiao/scan_code` });
+      if("stock" === type){
+        this.showDrawer = true;
+      }else{
+        this.build();
+      }
+    },
+    build(){
+      uni.showToast({
+        title: '功能建设中',
+        icon: 'none'
+      });
+    },
+    // 页面跳转
+    navigateTo(page) {
+      console.log('准备跳转到:', page);
+      // uni.navigateTo({ url: `/pages/${page}/${page}` });
+
+      if("stockRecords" === page){
+        uni.navigateTo({ url: `/pages/cjx/hexiao/ywy/add_goods_record` });
+      }else{
+        this.build();
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.page-container {
+  min-height: 100vh;
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/hexiao_bg.png");
+  background-size: contain;
+  background-position: center;
+  background-repeat: no-repeat;
+}
+.page-title {
+  color: #ffffff;
+  text-align: center;
+  font-size: 19px;
+  height: 160rpx;
+  line-height: 160rpx;
+}
+.header-section {
+  padding: 40rpx 40rpx 80rpx;
+  padding-top: 0rpx;
+  color: #ffffff;
+
+  .greeting-text {
+    font-size: 32rpx;
+    opacity: 0.9;
+  }
+
+  .title-text {
+    font-size: 42rpx;
+    font-weight: bold;
+    margin-top: 66rpx;
+  }
+}
+.content-grid {
+  padding: 0 30rpx;
+  margin-top: -40rpx;
+  position: relative;
+  z-index: 10;
+  // 使用CSS Grid布局
+  display: grid;
+  grid-template-columns: 1fr 1fr; // 两列等宽
+  gap: 20rpx; // 卡片间距
+
+}
+
+.card {
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/mini_bg.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-color: #ffffff;
+  border-radius: 20rpx;
+  padding: 25rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+  display: flex;
+  align-items: center;
+
+  .card-title {
+    font-size: 38rpx;
+    font-weight: 600;
+    color: #333;
+  }
+
+  .icon-wrapper {
+    width: 80rpx;
+    height: 80rpx;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 20rpx;
+  }
+}
+
+.large-card {
+  // 关键:让这个卡片跨越两行
+  grid-row: span 2;
+  height: 83%; // 确保高度自适应
+  position: relative;
+  flex-direction: column;
+  justify-content: space-between;
+  color: #ffffff;
+
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/lan_bg.png");
+  background-position: center;
+  background-repeat: no-repeat;
+
+  .large-card-text {
+    position: relative;
+    left: -18%;
+    top: 16px;
+    .card-title { color: #ffffff; }
+    .card-subtitle { font-size: 24rpx; opacity: 0.8; margin: 10rpx 0; }
+  }
+
+  .scan-button {
+    background-color: #ffffff;
+    color: #5E87FF;
+    font-size: 24rpx;
+    padding: 8rpx 20rpx;
+    border-radius: 30rpx;
+    display: inline-block;
+    margin-top: 20rpx;
+  }
+
+  .large-card-icon {
+    align-self: flex-end;
+    width: 140rpx;
+    height: 140rpx;
+    image { width: 100%; height: 100%; }
+  }
+}
+
+.small-card {
+  flex: 1;
+}
+
+.stat-card {
+  // 关键:让统计卡片默认只占一列,但如果需要跨列可以单独设置
+  // grid-column: span 2; // 如果需要单张卡片占满一行,则打开此行
+
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: flex-end;
+  position: relative;
+  padding-left: 40rpx;
+
+  &::before {
+    content: '';
+    position: absolute;
+    left: 20rpx;
+    top: 35rpx;
+    width: 8rpx;
+    height: 25rpx;
+    background-color: #6a8dff;
+    border-radius: 4rpx;
+  }
+
+  .stat-content {
+    .stat-title { font-size: 28rpx; color: #666; }
+    .stat-value { font-size: 44rpx; font-weight: bold; color: #333; margin: 10rpx 0; }
+    .stat-label { font-size: 24rpx; color: #999; }
+  }
+
+  .stat-image {
+    width: 120rpx;
+    position: absolute;
+    right: 20rpx;
+    bottom: 20rpx;
+  }
+}
+.mendian-card{
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/mendian.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  background-color: inherit;
+
+}
+.jifen-card{
+  background-size: 100% 100%;
+  background-image: url("https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/jifen.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-color: inherit;
+
+}
+</style>

+ 294 - 0
pages/cjx/hexiao/ywy/my.vue

@@ -0,0 +1,294 @@
+<template>
+  <view class="page-container">
+    <view class="profile-header">
+      <view class="avatar-wrapper">
+        <image class="avatar" :src="userInfo.avatarUrl" mode="aspectFill"></image>
+        <view class="badge">{{ userInfo.role }}</view>
+      </view>
+      <view class="phone-number">{{ userInfo.phone }}</view>
+    </view>
+
+    <view class="content-card">
+      <view class="list-item" @click="navigateToAction('points')">
+        <view class="item-left">
+          <image class="item-icon" :src="imgurl+'/cjx/hexiao/my_jifen.png'" mode="aspectFit"></image>
+          <text class="item-label">我的积分</text>
+        </view>
+        <view class="item-right">
+          <text class="item-value">{{ formatNumber(userInfo.points) }}</text>
+<!--          <uni-icons type="right" size="16" color="#c0c0c0"></uni-icons>-->
+        </view>
+      </view>
+
+      <view class="list-item" @click="navigateToAction('shops')">
+        <view class="item-left">
+          <image class="item-icon" :src="imgurl+'/cjx/hexiao/my_mendian.png'" mode="aspectFit"></image>
+          <text class="item-label">我的店铺</text>
+        </view>
+        <view class="item-right">
+          <text class="item-value">{{ userInfo.shopCount }}</text>
+<!--          <uni-icons type="right" size="16" color="#c0c0c0"></uni-icons>-->
+        </view>
+      </view>
+
+<!--      <view class="list-item">-->
+<!--        <view class="item-left">-->
+<!--          <uni-icons type="arrow-up-circle" size="22" color="#666"></uni-icons>-->
+<!--          <text class="item-label">当前版本</text>-->
+<!--        </view>-->
+<!--        <view class="item-right">-->
+<!--          <text class="item-value">{{ appVersion }}</text>-->
+<!--        </view>-->
+<!--      </view>-->
+
+<!--      <view class="list-item" @click="callSupport">-->
+<!--        <view class="item-left">-->
+<!--          <uni-icons type="phone-filled" size="22" color="#666"></uni-icons>-->
+<!--          <text class="item-label">客服电话</text>-->
+<!--        </view>-->
+<!--        <view class="item-right">-->
+<!--          <text class="item-value primary-color">{{ supportPhone }}</text>-->
+<!--        </view>-->
+<!--      </view>-->
+    </view>
+
+    <view class="footer">
+      <button class="logout-button" @click="logout">退出登录</button>
+    </view>
+    <CustomTabbar :current="2"/>
+  </view>
+</template>
+
+<script>
+import CustomTabbar from '@/components/cjx/tabbar_hexiao_ywy.vue';
+import {clearAdminToken, getAdminInfo} from "../../../../utils/auth";
+import {getAdminUserInfo} from "../../../../api/hexiao";
+
+export default {
+  components: {
+    CustomTabbar
+  },
+  data() {
+    return {
+      imgurl:"https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu",
+      // 用户信息 - 通常从登录后的storage或API获取
+      userInfo: {
+        avatarUrl: 'https://hyscancode.oss-cn-hangzhou.aliyuncs.com/xiaochengxu/cjx/hexiao/user-info.png', // 默认头像
+        phone: '',
+        role: '',
+        points: 0,
+        shopCount: 0
+      },
+      appVersion: '1.0',
+      supportPhone: '40012547856'
+    };
+  },
+  onLoad(){
+    let info = getAdminInfo();
+    this.userInfo.phone = info.phone;
+    this.userInfo.role = info.roleName;
+  },
+  onShow(){
+    getAdminUserInfo().then(res=>{
+      this.userInfo.shopCount = res.data.storeCount;
+      this.userInfo.points = res.data.pointTotal;
+    })
+  },
+  methods: {
+    // 格式化数字,添加逗号
+    formatNumber(num) {
+      if (num === undefined || num === null) return '';
+      return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+    },
+
+    // 列表项点击事件
+    navigateToAction(action) {
+      switch(action) {
+        case 'points':
+          console.log('跳转到我的积分页面');
+          uni.navigateTo({ url: '/pages/points/points' }); // 请替换为您的积分页面路径
+          break;
+        case 'shops':
+          console.log('跳转到我的店铺页面');
+          uni.navigateTo({ url: '/pages/shops/shops' }); // 请替换为您的店铺页面路径
+          break;
+      }
+    },
+
+    // 拨打客服电话
+    callSupport() {
+      uni.makePhoneCall({
+        phoneNumber: this.supportPhone,
+        success: () => {
+          console.log('拨打电话成功');
+        },
+        fail: (err) => {
+          console.log('拨打电话失败', err);
+          uni.showModal({
+            title: '拨号失败',
+            content: `无法拨打电话,请手动拨打客服热线:${this.supportPhone}`,
+            showCancel: false
+          })
+        }
+      });
+    },
+
+    // 退出登录
+    logout() {
+      uni.showModal({
+        title: '提示',
+        content: '您确定要退出登录吗?',
+        success: (res) => {
+          if (res.confirm) {
+            console.log('用户点击确定,执行退出登录操作');
+            clearAdminToken();
+            // 跳转到登录页
+            uni.reLaunch({
+              url: '/pages/cjx/hexiao/login' // 请替换为您的登录页路径
+            });
+          }
+        }
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+// 页面整体背景
+.page-container {
+  background-color: #f5f6fa;
+  min-height: 100vh;
+  position: relative;
+  box-sizing: border-box; /* 确保 padding 不会增加总高度 */
+
+  /* --- 关键改动 2 --- */
+  /* 增加底部内边距,为“退出登录”按钮和自定义tabBar都留出空间 */
+  &.with-padding-bottom {
+    padding-bottom: 280rpx;
+  }
+}
+
+// 顶部用户信息区
+.profile-header {
+  background: linear-gradient(135deg, #6ca1ff, #87d7dc);
+  height: 400rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding-bottom: 80rpx; // 为下方卡片留出重叠空间
+}
+
+.avatar-wrapper {
+  position: relative;
+
+  .avatar {
+    width: 150rpx;
+    height: 150rpx;
+    border-radius: 50%;
+    border: 4rpx solid rgba(255, 255, 255, 0.5);
+  }
+
+  .badge {
+    position: absolute;
+    bottom: 0;
+    right: -10rpx;
+    background-color: #ff9900;
+    color: #ffffff;
+    font-size: 22rpx;
+    padding: 4rpx 12rpx;
+    border-radius: 20rpx;
+    border: 2rpx solid #ffffff;
+  }
+}
+
+.phone-number {
+  color: #ffffff;
+  font-size: 36rpx;
+  font-weight: bold;
+  margin-top: 20rpx;
+  letter-spacing: 1rpx;
+}
+.item-icon {
+  width: 44rpx;
+  height: 44rpx;
+  margin-right: 15rpx; /* 图标和文字的间距 */
+}
+// 内容卡片
+.content-card {
+  background-color: #ffffff;
+  margin: -80rpx 30rpx 0 30rpx; // 负margin实现向上重叠效果
+  border-radius: 20rpx;
+  padding: 20rpx 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+  position: relative; // 确保在header之上
+  z-index: 10;
+}
+
+.list-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 35rpx 0;
+  border-bottom: 1rpx solid #f5f5f5;
+
+  &:last-child {
+    border-bottom: none;
+  }
+
+  .item-left {
+    display: flex;
+    align-items: center;
+
+    .item-label {
+      margin-left: 25rpx;
+      font-size: 30rpx;
+      color: #333;
+    }
+  }
+
+  .item-right {
+    display: flex;
+    align-items: center;
+
+    .item-value {
+      font-size: 28rpx;
+      color: #999;
+      margin-right: 10rpx;
+    }
+
+    .primary-color {
+      color: #6ca1ff; // 客服电话用主题色突出
+      font-weight: 500;
+    }
+  }
+}
+
+// 底部
+.footer {
+  position: fixed;
+  /* bottom值 = 自定义tabBar的高度(100rpx) + iPhone等机型的底部安全区 */
+  bottom: calc(100rpx + constant(safe-area-inset-bottom));
+  bottom: calc(100rpx + env(safe-area-inset-bottom));
+
+  left: 0;
+  width: 100%;
+  padding: 30rpx;
+  box-sizing: border-box;
+  background-color: #f5f6fa; // 设置背景色防止透明
+  z-index: 90; // 比 tabBar 低,比页面内容高
+}
+
+.logout-button {
+  background-color: #ffffff;
+  color: #555555;
+  border-radius: 50rpx;
+  font-weight: normal;
+  font-size: 32rpx;
+  border: 1rpx solid #e0e0e0;
+  &::after {
+    border: none;
+  }
+}
+</style>

+ 235 - 0
pages/cjx/hexiao/ywy/retail.vue

@@ -0,0 +1,235 @@
+<template>
+  <view class="page-container">
+    <view class="search-wrapper">
+      <view class="search-bar">
+        <uni-icons type="search" size="20" color="#999"></uni-icons>
+        <input
+            class="search-input"
+            v-model="searchQuery"
+            placeholder="输入门店名称"
+            placeholder-class="placeholder"
+           @confirm = "handleSearch"
+        />
+
+
+      </view>
+    </view>
+
+    <scroll-view scroll-y="true" class="list-container with-padding-bottom">
+      <view v-if="storeList.length === 0" class="empty-list">
+        <text>没有找到相关门店</text>
+      </view>
+      <view v-for="store in storeList" :key="store.id" class="store-card">
+        <view class="card-decorator"></view>
+        <view class="card-content">
+          <view class="card-header">
+            <uni-icons type="shop-filled" size="20" color="#3c82f8"></uni-icons>
+            <text class="store-name">{{ store.store_name }}</text>
+          </view>
+          <view class="info-grid">
+            <view class="info-row">
+              <text class="info-label">店主名称:</text>
+              <text class="info-value">{{ store.contact_name }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">联系方式:</text>
+              <text class="info-value">{{ store.contact_phone }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">门店地址:</text>
+              <text class="info-value">{{ store.address }}</text>
+            </view>
+          </view>
+          <view class="action-buttons">
+            <view class="action-btn" @click="viewDetails(store.id)">
+              <uni-icons type="eye-filled" size="18" color="#3c82f8"></uni-icons>
+              <text>详情</text>
+            </view>
+            <view class="action-btn" @click="editStore(store.id)">
+              <uni-icons type="compose" size="18" color="#3c82f8"></uni-icons>
+              <text>编辑</text>
+            </view>
+            <view class="action-btn" @click="patrolStore(store.id)">
+              <uni-icons type="home-filled" size="18" color="#3c82f8"></uni-icons>
+              <text>巡店</text>
+            </view>
+            <view class="action-btn delete-btn" @click="deleteStore(store.id)">
+              <uni-icons type="trash-filled" size="18" color="#e54d42"></uni-icons>
+              <text>删除</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </scroll-view>
+
+    <view class="fixed-footer">
+      <button class="add-store-btn" @click="addNewStore">新增门店</button>
+    </view>
+
+    <CustomTabbar :current="1"/>
+  </view>
+</template>
+
+<script>
+// 您自定义tabBar的路径,请确保正确
+import CustomTabbar from '@/components/cjx/tabbar_hexiao_ywy.vue';
+import {getStoreList,removeRetail} from "../../../../api/hexiao";
+export default {
+  components: {
+    CustomTabbar
+  },
+  data() {
+    return {
+      searchQuery: '',
+      storeList: [
+        // { id: 6, name: '湖北武汉光谷店6', owner: '张三丰', contact: '18812345678', address: '湖北省武汉市洪山区光谷步行街1号' },
+      ],
+      filteredList: [],
+      pagination: {
+        page: 1,
+        limit: 10,
+      },
+    };
+  },
+  onShow(){
+    this.fetchStoreList();
+  },
+  onLoad() {
+   this.fetchStoreList();
+  },
+  methods: {
+    fetchStoreList(isRefresh = false) {
+      if (this.isLoading || (this.loadStatus === 'noMore' && !isRefresh)) {
+        return; // 防止重复加载
+      }
+      if (isRefresh) {
+        this.pagination.page = 1;
+        this.storeList = [];
+        this.loadStatus = 'more';
+      }
+
+      this.isLoading = true;
+      this.loadStatus = 'loading';
+
+      // --- [模拟API请求] ---
+      // 在这里替换成您真实的 uni.request API 调用
+      console.log(`正在请求第 ${this.pagination.page} 页数据, 搜索词: "${this.searchQuery}"`);
+
+      getStoreList(this.pagination.page,this.pagination.limit,this.searchQuery).then(res=>{
+        let data = res.data;
+        let mockData = data.records;
+        if (mockData.length > 0) {
+          this.storeList = [...this.storeList, ...mockData];
+          this.pagination.page++;
+          this.loadStatus = mockData.length < this.pagination.limit ? 'noMore' : 'more';
+        } else {
+          this.loadStatus = 'noMore';
+        }
+        this.isLoading = false;
+      });
+    },
+    handleSearch() {
+        this.pagination.page = 1;
+        this.fetchStoreList();
+    },
+    viewDetails(id) {
+      uni.navigateTo({ url: '/pages/cjx/hexiao/add_retail?edit=0&id='+id });
+      console.log('查看详情 ID:', id);
+      },
+    editStore(id) {    uni.navigateTo({ url: '/pages/cjx/hexiao/add_retail?edit=1&id='+id });},
+    patrolStore(id) { console.log('巡店 ID:', id); },
+    deleteStore(id) {
+      let self = this;
+      uni.showModal({
+        title: '确认删除',
+        content: '您确定要删除这家门店吗?此操作不可恢复。',
+        confirmColor: '#e54d42',
+        success: (res) => {
+          if (res.confirm) {
+            removeRetail(id).then(() => {
+              self.resetSearch();
+              uni.showToast({ title: '删除成功', icon: 'success' });
+            });
+          }
+        }
+      });
+    },
+    resetSearch(){
+      this.fetchStoreList(true);
+    },
+    addNewStore() {
+      uni.navigateTo({ url: `/pages/cjx/hexiao/add_retail?edit=1` });
+     }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+  background-color: #f5f6fa;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+}
+
+.search-wrapper {
+  padding: 20rpx;
+  background-color: #ffffff;
+  position: sticky; top: 0; z-index: 100;
+}
+.search-bar {
+  display: flex; align-items: center; background-color: #f5f6fa;
+  border-radius: 50rpx; padding: 0 25rpx; height: 70rpx;
+}
+.search-input { flex: 1; font-size: 28rpx; margin-left: 15rpx; }
+.placeholder { color: #b0b0b0; }
+
+.list-container {
+  flex: 1; padding: 0 20rpx; box-sizing: border-box;
+}
+/* --- 关键改动 2 --- */
+.list-container.with-padding-bottom {
+  /* 按钮高度(90+40=130) + tabBar高度(100) + 一点富余 = 250rpx */
+  padding-bottom: 250rpx;
+}
+
+.empty-list { text-align: center; color: #999; padding-top: 100rpx; }
+.store-card { background-color: #ffffff; border-radius: 16rpx; margin-top: 20rpx; position: relative; overflow: hidden; box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04); }
+.card-decorator { position: absolute; left: 0; top: 0; bottom: 0; width: 8rpx; background-color: #e3efff; }
+.card-content { padding: 30rpx; padding-left: 40rpx; }
+.card-header { display: flex; align-items: center; }
+.store-name { font-size: 32rpx; font-weight: bold; color: #333; margin-left: 15rpx; }
+.info-grid { margin-top: 20rpx; font-size: 26rpx; color: #666; }
+.info-row { margin-top: 10rpx; }
+.action-buttons { display: flex; justify-content: space-around; border-top: 1rpx solid #f5f5f5; margin-top: 30rpx; padding-top: 25rpx; }
+.action-btn { display: flex; align-items: center; font-size: 26rpx; color: #3c82f8; }
+.action-btn text { margin-left: 8rpx; }
+.delete-btn { color: #e54d42; }
+
+.fixed-footer {
+  position: fixed;
+  /* --- 关键改动 1 --- */
+  /* bottom值 = 自定义tabBar的高度(100rpx) + iPhone等机型的底部安全区 */
+  bottom: calc(100rpx + constant(safe-area-inset-bottom));
+  bottom: calc(100rpx + env(safe-area-inset-bottom));
+
+  left: 0;
+  width: 100%;
+  background-color: #f5f6fa;
+  padding: 20rpx 30rpx;
+  box-sizing: border-box;
+  z-index: 90; // z-index比tabBar低,但比页面内容高
+}
+
+.add-store-btn {
+  background-color: #3c82f8;
+  color: white;
+  border-radius: 50rpx;
+  font-size: 32rpx;
+  height: 90rpx;
+  line-height: 90rpx;
+  &::after {
+    border: none;
+  }
+}
+</style>

+ 275 - 0
pages/cjx/hexiao/ywy/scan_code.vue

@@ -0,0 +1,275 @@
+<template>
+  <view class="page-container">
+    <view class="camera-wrapper">
+      <camera mode= "scanCode" device-position="back" flash="off" @scancode="onScanCodeSuccess" @error="onCameraError" style="width: 100%; height: 450rpx;"></camera>
+    </view>
+
+    <view class="scan-result-card">
+      <view class="card-header">
+        <uni-icons type="scan" size="20" color="#333"></uni-icons>
+        <text class="header-title">已扫二维码</text>
+        <view class="count-badge">
+          <text>总数:{{ totalCount }}个</text>
+        </view>
+      </view>
+
+      <scroll-view scroll-y="true" class="code-list-scroll">
+        <view class="code-list-grid">
+          <view v-if="numberList.length === 0" class="empty-tip">等待扫描...</view>
+          <view class="code-item" v-for="(code, index) in numberList" :key="index">
+            {{ formatIndex(index + 1) }}.{{ code }}
+          </view>
+        </view>
+      </scroll-view>
+
+      <view class="footer-buttons">
+        <button class="btn clear-btn" @click="clearAll">全部清空</button>
+        <button class="btn submit-btn" @click="submit">提交</button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import {getQrcodeNum } from '@/api/hexiao.js';
+import {active} from "../../../../api/hexiao";
+
+export default {
+  data() {
+    return {
+      // 列表初始为空
+      codeList: [],
+      storeId:0,
+      numberList: [],
+      // 用于播放提示音
+      audioContext: null
+    };
+  },
+  onReady() {
+    // 提前创建好音频上下文,提高性能
+    // 您需要在 static 目录下放置一个提示音文件,例如 beep.mp3
+    this.audioContext = uni.createInnerAudioContext();
+    this.audioContext.src = '/static/beep.mp3'; // 请替换为您的提示音文件路径
+  },
+  onLoad(opt) {
+    this.storeId = opt.storeId
+    // 获取二维码列表
+    // this.codeList = this.$store.state.qrCodeList;
+  },
+  computed: {
+    totalCount() {
+      return this.codeList.length;
+    }
+  },
+  methods: {
+    getCodeNumber(code){
+      if (this.codeList.includes(code)) {
+        uni.showToast({
+          title: '该码已存在',
+          icon: 'none'
+        });
+        return;
+      }
+      uni.showLoading();
+      getQrcodeNum(code).then(res=>{
+        uni.hideLoading();
+        if(res.code === 0){
+          let data = res.data;
+          //检查是否已存在,防止重复添加
+          if (this.codeList.includes(code)) {
+            uni.showToast({
+              title: '该码已存在',
+              icon: 'none'
+            });
+            return;
+          }
+
+          // 添加到列表顶部
+          this.codeList.unshift(code);
+          this.numberList.unshift(data);
+          // 给予用户反馈
+          this.playBeep();
+          uni.vibrateShort();
+        }else{
+          uni.showToast({
+            title: "该码有误",
+            icon: 'none'
+          });
+        }
+      });
+
+    },
+    // 3. 处理相机扫码成功的事件
+    onScanCodeSuccess(e) {
+      const code = e.detail.result;
+      console.log('扫描到的内容:', code);
+      this.getCodeNumber(code)
+
+    },
+
+    // 相机初始化失败
+    onCameraError(e) {
+      console.log(e.detail);
+      uni.showModal({
+        title: '相机错误',
+        content: '无法启动相机,请检查权限或重启应用',
+        showCancel: false
+      })
+    },
+
+    // 播放提示音
+    playBeep() {
+      this.audioContext.play();
+    },
+
+    // 格式化序号
+    formatIndex(num) {
+      return num < 10 ? '0' + num : num;
+    },
+
+    // 清空事件
+    clearAll() {
+      if (this.totalCount === 0) return;
+      uni.showModal({
+        title: '确认',
+        content: '您确定要清空所有已扫描的二维码吗?',
+        success: (res) => {
+          if (res.confirm) {
+            this.codeList = [];
+            this.numberList = [];
+            uni.showToast({ title: '已清空', icon: 'success' });
+          }
+        }
+      });
+    },
+
+    // 提交事件
+    submit() {
+      if (this.totalCount === 0) {
+        uni.showToast({ title: '没有可提交的内容', icon: 'none' });
+        return;
+      }
+      console.log('提交的二维码列表:', this.codeList);
+      uni.showLoading({ title: '提交中...' });
+      let obj = {};
+      obj.storeId = 1;
+      obj.qrcodeIds = this.codeList;
+      active(obj).then(res=>{
+        uni.hideLoading();
+        if(res.code == 0){
+          uni.showToast({ title: '操作成功', icon: 'none' });
+          this.numberList = []
+          this.codeList = [];
+          uni.navigateBack();
+        }else{
+          uni.showToast({ title: res.msg, icon: 'none' });
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+/* 1. 页面整体使用flex布局,高度占满屏幕 */
+.page-container {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f6fa;
+}
+
+.camera-wrapper {
+  // 相机区域高度固定
+  height: 450rpx;
+}
+
+/* 1. 卡片区域自动撑满剩余空间 */
+.scan-result-card {
+  flex: 1; /* flex: 1 是关键,让此元素占据所有剩余空间 */
+  display: flex; /* 内部也使用flex布局,方便内容区滚动 */
+  flex-direction: column;
+
+  background-color: #ffffff;
+  margin: 0 20rpx 20rpx 20rpx;
+  border-radius: 20rpx;
+  padding: 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
+  overflow: hidden; /* 防止内容溢出圆角 */
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  padding-bottom: 25rpx;
+  border-bottom: 1rpx solid #f0f0f0;
+  // 头部高度固定,不参与flex缩放
+  flex-shrink: 0;
+}
+
+.header-title {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+  margin-left: 15rpx;
+}
+
+.count-badge {
+  background-color: #e3efff;
+  color: #409eff;
+  font-size: 24rpx;
+  padding: 6rpx 15rpx;
+  border-radius: 30rpx;
+  margin-left: 20rpx;
+  font-weight: 500;
+}
+
+/* 2. 列表滚动区域 */
+.code-list-scroll {
+  flex: 1; /* 关键:让滚动区域占据头部和底部之间的所有空间 */
+  // height: 0;  // flex布局中的一个常用技巧,确保flex:1正常工作
+  padding: 20rpx 0;
+}
+
+.empty-tip {
+  text-align: center;
+  color: #999;
+  padding-top: 100rpx;
+}
+
+.code-list-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 20rpx 40rpx;
+}
+
+.code-item {
+  font-size: 28rpx;
+  color: #606266;
+  word-break: break-all;
+}
+
+.footer-buttons {
+  display: flex;
+  justify-content: space-between;
+  gap: 30rpx;
+  padding-top: 20rpx;
+  border-top: 1rpx solid #f0f0f0;
+  // 底部高度固定,不参与flex缩放
+  flex-shrink: 0;
+}
+
+.btn {
+  flex: 1;
+  margin: 0;
+  font-size: 30rpx;
+  border-radius: 40rpx;
+  height: 80rpx;
+  line-height: 80rpx;
+  background-color: #409eff;
+  color: #ffffff;
+  &::after {
+    border: none;
+  }
+}
+</style>

BIN
static/beep.mp3


+ 29 - 0
utils/auth.js

@@ -1,5 +1,8 @@
 const TokenKey = 'App-Token'
 
+const AdminTokenKey = 'App-Admin-Token'
+const AdminInfoToken = 'App-Admin-Info'
+
 export function getToken() {
   return uni.getStorageSync(TokenKey)
 }
@@ -8,6 +11,32 @@ export function setToken(token) {
   return uni.setStorageSync(TokenKey, token)
 }
 
+
+export function clearAdminToken() {
+    uni.removeStorageSync(AdminInfoToken)
+    return uni.removeStorageSync(AdminTokenKey)
+}
+
+export function getAdminToken() {
+    return uni.getStorageSync(AdminTokenKey)
+}
+
+
+export function setAdminToken(token) {
+    return uni.setStorageSync(AdminTokenKey,token)
+}
+
+export function setAdminInfo(token) {
+    return uni.setStorageSync(AdminInfoToken,token)
+}
+
+export function getAdminInfo(token) {
+    return uni.getStorageSync(AdminInfoToken)
+}
+
+
+
+
 export function removeToken() {
 	uni.removeStorageSync("scanDetail");
 	uni.removeStorageSync("scanCode");

+ 105 - 0
utils/requestAdmin.js

@@ -0,0 +1,105 @@
+import store from '@/store'
+import config from '@/config'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import { toast, showConfirm, tansParams } from '@/utils/common'
+import {getAdminToken} from "./auth";
+
+let timeout = 10000
+const baseUrl = config.baseUrl
+
+const request = config => {
+	console.log(44444)
+  // 是否需要设置 token
+  const isToken = (config.headers || {}).isToken === false
+  config.header = config.header || {}
+  if (getAdminToken() && !isToken) {
+    config.header['sessionId'] = getAdminToken()
+  }
+  // get请求映射params参数
+  if (config.params) {
+    let url = config.url + '?' + tansParams(config.params)
+    url = url.slice(0, -1)
+    config.url = url
+  }
+
+  return new Promise(resolve => {
+    uni.request({
+      method: config.method || 'get',
+      timeout: config.timeout || timeout,
+      url: config.baseUrl || baseUrl + config.url,
+      data: config.data,
+      header: config.header,
+      dataType: 'json'
+    }).then(response => {
+      const error = response.statusCode !== 200
+      const res = response
+	  console.log(response)
+      if (error) {
+        toast('后端接口连接异常')
+        resolve({
+          success: false,
+          code: response.statusCode,
+          msg: '后端接口连接异常',
+          data: null
+        })
+        return
+      }
+
+      const code = res.data.code !== undefined ? res.data.code : 200
+      const msg = res.data.msg || errorCode[code] || errorCode.default
+
+      // 构造统一返回结构
+      const result = {
+        success: code === 0,
+        code,
+        msg,
+        data: res.data.data || null,
+        raw: res.data
+      }
+
+      if (code === 401  || code === -10) {
+        if (getToken()) {
+          store.dispatch('LogOut').then(() => {
+            uni.reLaunch({ url: '/pages/login' })
+          })
+        }
+        result.success = false
+        resolve(result)
+        return
+      }
+
+      if (code === 500) {
+        result.success = false
+        resolve(result)
+        return
+      }
+
+      if (code !== 0) {
+        result.success = false
+        resolve(result)
+        return
+      }
+
+      resolve(result)
+    }).catch(error => {
+      let { message } = error
+      if (message === 'Network Error') {
+        message = '后端接口连接异常'
+      } else if (message.includes('timeout')) {
+        message = '系统接口请求超时'
+      } else if (message.includes('Request failed with status code')) {
+        message = '系统接口' + message.substr(message.length - 3) + '异常'
+      }
+      toast(message)
+      resolve({
+        success: false,
+        code: -999,
+        msg: message,
+        data: null
+      })
+    })
+  })
+}
+
+export default request