Compare commits

..

27 Commits

Author SHA1 Message Date
76cfa6b278 修复内容 2026-01-30 18:48:13 +08:00
5fe5289e26 修复内容 2026-01-18 18:07:53 +08:00
5455aceab5 修复内容 2026-01-17 16:44:21 +08:00
41ad75af08 修复内容 2026-01-16 18:26:15 +08:00
daae84b827 修改订单查询状态 2026-01-15 22:01:13 +08:00
f7328c614a 添加个人中心功能 2026-01-15 20:44:03 +08:00
039055838e 添加个人中心功能
添加订单记录
2026-01-14 19:05:51 +08:00
dfec5819aa 添加个人中心功能
添加订单记录
2026-01-14 18:05:59 +08:00
e658c2a0fd 添加日历 2026-01-08 17:55:47 +04:00
7c34efae5c 修改评价 2026-01-07 17:27:51 +08:00
c1220e7b8f 修改商品分类缓存 2026-01-07 10:31:16 +08:00
14016c4144 修改订单逻辑 2026-01-06 16:48:15 +08:00
b984ed1dba 地图api 2026-01-01 16:03:20 +08:00
09f117e59c 地图api 2026-01-01 15:19:18 +08:00
b8a9f9b62e 添加统计逻辑 2025-12-29 17:28:14 +08:00
1e542a55bf 修改微信登录查询配置文件 2025-12-27 15:13:35 +08:00
08992851e0 修改微信登录查询配置文件 2025-12-27 14:10:53 +08:00
6963216a85 添加根据经纬度获取城市
添加后台统计看板
2025-12-26 16:26:15 +08:00
028886c1fe 添加发票 2025-12-26 09:26:43 +08:00
56f951d0bd 添加发票 2025-12-25 17:45:20 +08:00
16d1adacca 后台:
添加服务逻辑的编辑
接口:
添加服务列表
添加服务详情
添加服务点赞、收藏
2025-12-24 23:03:48 +08:00
49969d813a 添加跨域 2025-12-23 11:05:06 +08:00
2f79f7fbcd 添加跨域
添加服务后台添加
2025-12-23 10:40:05 +08:00
3482382734 添加跨域
添加服务后台添加
2025-12-23 10:38:20 +08:00
3f6112d6f1 添加跨域
添加服务后台添加
2025-12-23 10:28:15 +08:00
eeb443b225 添加跨域
添加服务后台添加
2025-12-23 10:27:54 +08:00
c61a34ec7d 添加会员功能 2025-12-22 23:37:52 +08:00
93 changed files with 7951 additions and 1308 deletions

View File

@@ -42,9 +42,21 @@ class Ad extends AdminBase
}
$this->_error($result);
}
$this->assign('category_list',AdLogic::getGoodsCategory());
// 确保 client 参数有效
if (empty($client)) {
$client = $this->request->param('client', '1');
}
$category_list = AdLogic::getGoodsCategory();
$link_page = \app\common\model\Ad::getLinkPage($client);
$position_list = AdLogic::infoPosition($client);
$this->assign('category_list', AdLogic::getGoodsCategory());
$this->assign('link_page', \app\common\model\Ad::getLinkPage($client));
$this->assign('position_list', AdLogic::infoPosition($client));
$this->assign([
'category_list' => $category_list,
'link_page' => $link_page,
'position_list' => $position_list,
]);
return $this->fetch();
}

View File

@@ -0,0 +1,485 @@
<?php
namespace app\admin\controller;
use app\admin\model\OrderGoods;
use app\admin\model\OrderRecharge;
use app\admin\model\OrderYearcard;
use app\admin\model\User;
use app\common\server\UrlServer;
use think\Db;
use think\facade\Url;
use app\admin\model\Order;
class BulletinBoard extends AdminBase
{
//看板统计
public function index()
{
// 本月与上月时间范围
$monthStart = date('Y-m-01 00:00:00');
$monthEnd = date('Y-m-t 23:59:59');
$lastMonthStart = date('Y-m-01 00:00:00', strtotime('-1 month'));
$lastMonthEnd = date('Y-m-t 23:59:59', strtotime('-1 month'));
$monthStartTs = strtotime($monthStart);
$monthEndTs = strtotime($monthEnd);
//查询商品订单
$orderMoney = Order::where('status','<>', 0)->sum('order_amount');
//年卡订单
$yearCardOrderMoney = OrderYearcard::where('status', 1)->sum('pay_fee');
//订单管理
$orderGoods = OrderGoods::where('status', 1)->sum('total_fee');
//充值订单
$rechargeOrderMoney = OrderRecharge::where('pay_status', 1)->sum('order_amount');
// 计算月销售总额(所有订单类型的总和)
$monthlySales = ($orderMoney ?: 0) + ($yearCardOrderMoney ?: 0) + ($orderGoods ?: 0) + ($rechargeOrderMoney ?: 0);
// 计算月订单量(本月订单数量)
$monthlyOrders = Order::where('status','<>', 0)
->whereTime('create_time', 'month')
->count();
$monthlyOrders += OrderYearcard::where('status', 1)
->whereTime('createtime', 'month')
->count();
$monthlyOrders += OrderGoods::where('status', 1)
->whereTime('createtime', 'month')
->count();
$monthlyOrders += OrderRecharge::where('pay_status', 1)
->whereTime('create_time', 'month')
->count();
// 计算月客户数(本月新增用户)
$monthlyCustomers = User::whereTime('create_time', 'month')
->where('del', 0)
->count();
// 计算同比(上个月的数据)
$lastMonthSales = Order::where('status','<>', 0)
->whereTime('create_time', 'last month')
->sum('order_amount');
$lastMonthSales += OrderYearcard::where('status', 1)
->whereTime('createtime', 'last month')
->sum('pay_fee');
$lastMonthSales += OrderGoods::where('status', 1)
->whereTime('createtime', 'last month')
->sum('total_fee');
$lastMonthSales += OrderRecharge::where('pay_status', 1)
->whereTime('create_time', 'last month')
->sum('order_amount');
$lastMonthOrders = Order::where('status','<>', 0)
->whereTime('create_time', 'last month')
->count();
$lastMonthOrders += OrderYearcard::where('status', 1)
->whereTime('createtime', 'last month')
->count();
$lastMonthOrders += OrderGoods::where('status', 1)
->whereTime('createtime', 'last month')
->count();
$lastMonthOrders += OrderRecharge::where('pay_status', 1)
->whereTime('create_time', 'last month')
->count();
$lastMonthCustomers = User::whereTime('create_time', 'last month')
->where('del', 0)
->count();
// 计算同比变化率
$salesCompare = $lastMonthSales > 0 ? round((($monthlySales - $lastMonthSales) / $lastMonthSales) * 100, 2) : 0;
$ordersCompare = $lastMonthOrders > 0 ? round((($monthlyOrders - $lastMonthOrders) / $lastMonthOrders) * 100, 2) : 0;
$customersCompare = $lastMonthCustomers > 0 ? round((($monthlyCustomers - $lastMonthCustomers) / $lastMonthCustomers) * 100, 2) : 0;
// 统计本月客户分析(老客户 vs 新客户)
// 本月所有用户ID列表
$monthUsers = User::whereTime('create_time', 'month')
->where('del', 0)
->column('id');
// 如果本月有用户,查询这些用户中哪些有订单(老客户)
$oldCustomerCount = 0;
$newCustomerCount = 0;
if (!empty($monthUsers)) {
// 查询本月注册的用户中在Order表中有订单的用户老客户
// 只要有订单就算老客户,不限制订单时间
$oldCustomerIds = Order::where('status','<>', 0)
->where('user_id', 'in', $monthUsers)
->where('user_id', '>', 0)
->distinct(true)
->column('user_id');
$oldCustomerCount = count($oldCustomerIds);
// 本月注册但没下过单的(新客户)
$newCustomerCount = count($monthUsers) - $oldCustomerCount;
}
// ========== 目标完成率与客服进度 ==========
// 销售目标(可以后续做成后台可配置,这里先写死为 100000
$monthlyTarget = 100000;
$overallCompletion = $monthlyTarget > 0 ? round(($monthlySales / $monthlyTarget) * 100, 2) : 0;
// 按客服统计本月与上月销售额
$staffCurrent = Db::name('order')
->alias('o')
->join('admin a', 'o.admin_id = a.id', 'LEFT')
->where('o.status', '<>', 0)
->whereTime('o.create_time', 'between', [$monthStart, $monthEnd])
->field('o.admin_id,a.name as staff_name,SUM(o.order_amount) as total')
->group('o.admin_id')
->select();
$staffLast = Db::name('order')
->alias('o')
->join('admin a', 'o.admin_id = a.id', 'LEFT')
->where('o.status', '<>', 0)
->whereTime('o.create_time', 'between', [$lastMonthStart, $lastMonthEnd])
->field('o.admin_id,SUM(o.order_amount) as total')
->group('o.admin_id')
->select();
$staffLastMap = [];
foreach ($staffLast as $row) {
$staffLastMap[$row['admin_id']] = $row['total'];
}
$staffProgress = [];
foreach ($staffCurrent as $row) {
$adminId = $row['admin_id'];
$staffSales = (float)$row['total'];
$lastSales = isset($staffLastMap[$adminId]) ? (float)$staffLastMap[$adminId] : 0;
$percent = $monthlySales > 0 ? round(($staffSales / $monthlySales) * 100, 2) : 0;
$yoy = $lastSales > 0 ? round((($staffSales - $lastSales) / $lastSales) * 100, 2) : 0;
$staffProgress[] = [
'name' => $row['staff_name'] ?: '未分配',
'sales' => $staffSales,
'percent' => $percent,
'yoy' => $yoy,
];
}
// ========== 渠道分析(当月订单数按渠道) ==========
$channelRows = Db::name('order')
->alias('o')
->join('orderchannel c', 'o.channel_id = c.id', 'LEFT')
->where('o.status', '<>', 0)
->whereTime('o.create_time', 'month')
->field('c.name as channel_name, COUNT(*) as total')
->group('o.channel_id')
->select();
$channelPie = [];
foreach ($channelRows as $row) {
$name = $row['channel_name'] ?: '其他';
$value = (int)$row['total'];
$channelPie[] = [
'name' => $name,
'value' => $value,
];
}
// ========== 每日销售趋势(仅商品订单,按天汇总) ==========
$trendRows = Db::name('order')
->where('status', '<>', 0)
->whereTime('create_time', 'between', [$monthStart, $monthEnd])
->field("FROM_UNIXTIME(create_time,'%Y-%m-%d') as day, SUM(order_amount) as total")
->group('day')
->order('day asc')
->select();
$trendDates = [];
$trendValues = [];
$trendTotal = 0;
foreach ($trendRows as $row) {
$trendDates[] = $row['day'];
$amount = (float)$row['total'];
$trendValues[] = $amount;
$trendTotal += $amount;
}
// 上月总销售额(用于计算趋势图上的“上月同比”)
$lastTrendTotal = Db::name('order')
->where('status', '<>', 0)
->whereTime('create_time', 'between', [$lastMonthStart, $lastMonthEnd])
->sum('order_amount');
$trendCompare = $lastTrendTotal > 0 ? round((($trendTotal - $lastTrendTotal) / $lastTrendTotal) * 100, 2) : 0;
// ========== 区域月销售额(通过地址模糊匹配简单统计) ==========
$regions = ['南明区', '云岩区', '白云区', '乌当区', '花溪区', '龙里'];
$regionTotals = [];
foreach ($regions as $regionName) {
$total = Db::name('order')
->where('status', '<>', 0)
->whereTime('create_time', 'month')
->whereLike('address', '%' . $regionName . '%')
->sum('order_amount');
$regionTotals[] = (float)$total;
}
// ========== 本月销售情况(单次/年卡/次卡/其它服务) ==========
// 复用统计图表控制器中的规则:根据 number 字段区分不同卡类型
$salesSituation = [
['value' => 0, 'name' => '单次服务'],
['value' => 0, 'name' => '年卡销量'],
['value' => 0, 'name' => '次卡销量'],
['value' => 0, 'name' => '其它服务'],
];
$kaWhere = [
['del', '=', 0],
['create_time', '>=', $monthStartTs],
['create_time', '<=', $monthEndTs],
];
// 单次服务number = 1
$salesSituation[0]['value'] = Db::name('order')
->where($kaWhere)
->where('number', '=', 1)
->sum('total_amount');
// 年卡number > 6
$salesSituation[1]['value'] = Db::name('order')
->where($kaWhere)
->where('number', '>', 6)
->sum('total_amount');
// 次卡2 <= number <= 6
$salesSituation[2]['value'] = Db::name('order')
->where($kaWhere)
->where('number', '>=', 2)
->where('number', '<=', 6)
->sum('total_amount');
// 其它服务number = 0
$salesSituation[3]['value'] = Db::name('order')
->where($kaWhere)
->where('number', '=', 0)
->sum('total_amount');
// ========== 渠道 & 区域详细拆分(总额 / 年卡 / 次卡 / 单次 / 其它) ==========
$serviceTypes = [
'total' => [],
'year' => [['number', '>', 6]],
'times' => [['number', '>=', 2], ['number', '<=', 6]],
'single'=> [['number', '=', 1]],
'other' => [['number', '=', 0]],
];
// 渠道关键字到列 key 的映射
$channelBuckets = [
'meituan' => '美团',
'gzh' => '公众号',
'douyin' => '抖音',
'staff' => '员工',
'yeye' => '异业',
'other' => '渠道',
];
$channelDetail = [];
foreach ($serviceTypes as $typeKey => $extraWhere) {
$totals = [
'meituan' => 0,
'gzh' => 0,
'douyin' => 0,
'staff' => 0,
'yeye' => 0,
'other' => 0,
];
$query = Db::name('order')
->alias('o')
->join('orderchannel c', 'o.channel_id = c.id', 'LEFT')
->where('o.status', '<>', 0)
->whereBetween('o.create_time', [$monthStartTs, $monthEndTs]);
if (!empty($extraWhere)) {
$query->where($extraWhere);
}
$rows = $query
->field('c.name as channel_name, SUM(o.order_amount) as total')
->group('o.channel_id')
->select();
foreach ($rows as $row) {
$name = $row['channel_name'] ?? '';
$value = (float)($row['total'] ?? 0);
$bucket = 'other';
if (mb_strpos($name, '美团') !== false) {
$bucket = 'meituan';
} elseif (mb_strpos($name, '公众号') !== false || mb_strpos($name, '公') !== false) {
$bucket = 'gzh';
} elseif (mb_strpos($name, '抖音') !== false) {
$bucket = 'douyin';
} elseif (mb_strpos($name, '员工') !== false) {
$bucket = 'staff';
} elseif (mb_strpos($name, '异业') !== false) {
$bucket = 'yeye';
}
$totals[$bucket] += $value;
}
$channelDetail[$typeKey] = $totals;
}
// 区域明细:每种服务类型在各区域的销售额
$regionDetail = [];
foreach ($serviceTypes as $typeKey => $extraWhere) {
$regionDetail[$typeKey] = [];
foreach ($regions as $regionName) {
$q = Db::name('order')
->where('status', '<>', 0)
->whereBetween('create_time', [$monthStartTs, $monthEndTs])
->whereLike('address', '%' . $regionName . '%');
if (!empty($extraWhere)) {
$q->where($extraWhere);
}
$regionDetail[$typeKey][$regionName] = (float)$q->sum('order_amount');
}
}
// 汇总仪表盘数据供前端使用
$dashboardData = [
'monthlyTarget' => $monthlyTarget,
'overallCompletion' => $overallCompletion,
'staffProgress' => $staffProgress,
'channelPie' => $channelPie,
'trendDates' => $trendDates,
'trendValues' => $trendValues,
'trendCompare' => $trendCompare,
'regions' => $regions,
'regionTotals' => $regionTotals,
'salesSituation' => $salesSituation,
'channelDetail' => $channelDetail,
'regionDetail' => $regionDetail,
];
// 传递数据到视图
$this->assign([
'monthlySales' => $monthlySales,
'monthlyOrders' => $monthlyOrders,
'monthlyCustomers' => $monthlyCustomers,
'salesCompare' => $salesCompare,
'ordersCompare' => $ordersCompare,
'customersCompare' => $customersCompare,
'oldCustomers' => $oldCustomerCount, // 老客户数量(本月注册且下过单的)
'newCustomers' => $newCustomerCount, // 新客户数量(本月注册但没下过单的)
'dashboardData' => json_encode($dashboardData, JSON_UNESCAPED_UNICODE),
]);
return $this->fetch();
}
//地图上显示注册用户位置
public function map()
{
//查询商品订单
$orderMoney = Order::where('status','<>', 0)->sum('order_amount');
//年卡订单
$yearCardOrderMoney = OrderYearcard::where('status', 1)->sum('pay_fee');
//订单管理
$orderGoods = OrderGoods::where('status', 1)->sum('total_fee');
//充值订单
$rechargeOrderMoney = OrderRecharge::where('pay_status', 1)->sum('order_amount');
return $this->fetch();
}
public function getUserMap()
{
// 查询有经纬度的用户
$userArray = User::where('latitude', '<>', '')
->where('longitude', '<>', '')
->field('id,nickname,mobile,avatar,longitude,latitude')
->select();
// 格式化数据,转换为前端期望的字段名
$result = [];
foreach ($userArray as $user) {
// 处理头像URL使用代理接口避免CORS问题
$avatar = '';
if (!empty($user['avatar'])) {
$originalUrl = UrlServer::getFileUrl($user['avatar']);
// 去掉JSON编码时产生的转义反斜杠
$originalUrl = str_replace('\\/', '/', $originalUrl);
// 如果URL是跨域的使用代理接口
$currentDomain = $this->request->domain();
if (strpos($originalUrl, $currentDomain) === false) {
// 跨域使用代理手动构建URL避免双重编码
$avatar = $this->request->domain() . '/admin/bulletin_board/proxyImage?url=' . rawurlencode($originalUrl);
} else {
// 同域,直接使用
$avatar = $originalUrl;
}
}
$result[] = [
'id' => $user['id'] ?? 0,
'lng' => floatval($user['longitude'] ?? 0),
'lat' => floatval($user['latitude'] ?? 0),
'name' => $user['nickname'] ?? '',
'contact' => $user['nickname'] ?? '', // 兼容两种字段名
'mobile' => $user['mobile'] ?? '',
'phone' => $user['mobile'] ?? '', // 兼容 telephone/phone
'telephone' => $user['mobile'] ?? '',
'avatar' => $avatar
];
}
return $this->_success('获取成功', $result);
}
/**
* 图片代理接口解决CORS跨域问题
*/
public function proxyImage()
{
$url = $this->request->get('url', '');
if (empty($url)) {
header('HTTP/1.1 404 Not Found');
exit;
}
// 解码URL
$url = urldecode($url);
// 验证URL格式
if (!filter_var($url, FILTER_VALIDATE_URL)) {
header('HTTP/1.1 400 Bad Request');
exit;
}
// 只允许图片格式
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$extension = strtolower(pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) {
header('HTTP/1.1 403 Forbidden');
exit;
}
// 获取图片
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => 10,
'header' => [
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
]
]
]);
$imageData = @file_get_contents($url, false, $context);
if ($imageData === false) {
header('HTTP/1.1 404 Not Found');
exit;
}
// 设置响应头
header('Content-Type: image/' . ($extension === 'jpg' ? 'jpeg' : $extension));
header('Cache-Control: public, max-age=3600');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET');
// 输出图片
echo $imageData;
exit;
}
}

View File

@@ -0,0 +1,239 @@
<?php
namespace app\admin\controller;
use app\admin\model\Invoice as InvoiceModel;
use app\admin\model\InvoiceLog;
use app\common\server\UrlServer;
use think\Db;
class Invoice extends AdminBase
{
/**
* 发票列表
*/
public function lists()
{
if ($this->request->isAjax()) {
$get = $this->request->get();
// 构建查询条件
$where = [];
if (!empty($get['keyword'])) {
$where[] = ['oid|invoice_id', 'like', '%' . $get['keyword'] . '%'];
}
if (isset($get['state']) && $get['state'] !== '') {
$where[] = ['state', '=', intval($get['state'])];
}
// 查询数据(关联订单表获取订单编号)
$list = Db::name('invoice_log')
->where($where)
->order('state asc')
->select();
// 格式化数据
$currentDomain = $_SERVER['HTTP_HOST'];
$result = [];
foreach ($list as $item) {
$result[] = [
'id' => $item['id'] ?? 0,
'invoice_id' => $item['invoice_id'] ?? 0,
'oid' => $item['oid'] ?? 0,
'state' => $item['state'] ?? 1,
'document' => "http://".$currentDomain."/public/".$item['document'] ?? '',
'create_time' => $item['create_time'] ?? '',
'update_time' => $item['update_time'] ?? '',
];
}
$data = [
'count' => count($result),
'lists' => $result
];
$this->_success('', $data);
}
return $this->fetch();
}
/**
* 查看详情
*/
public function info()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
$this->_error('参数错误');
}
$invoice = InvoiceLog::with([
"order" => function ($query) {
$query->field('id, order_sn');
},
"invoice" => function ($query) {
$query->with('user');
},
])
->where('id', $id)
->find();
if (empty($invoice)) {
$this->_error('数据不存在');
}
// 处理文件路径转换为完整URL
$currentDomain = $_SERVER['HTTP_HOST'];
if (!empty($invoice['document'])) {
$invoice['document_url'] = "http://".$currentDomain."/public/".$invoice['document'];
}
$this->assign([
'invoice' => $invoice,
]);
return $this->fetch();
}
/**
* 上传
*/
public function edit()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
$this->_error('参数错误');
}
if ($this->request->isAjax() && $this->request->isPost()) {
$post = $this->request->post();
// 表单验证
if (empty($post['invoice_id'])) {
$this->_error('请输入发票信息ID');
}
if (empty($post['document'])){
$this->_error('请上传发票文件');
}
$data = [
'state' => $post['state'],
'document' => $post['document'],
'update_time' => date('Y-m-d H:i:s', time()),
];
// 更新数据
$result = Db::name('invoice_log')->where('id', $id)->update($data);
if ($result) {
$this->_success('上传成功');
} else {
$this->_error('上传失败');
}
}
// 查询订单列表
$orders = Db::name('order')
->where('order_status', '>', 0)
->field('id, order_sn')
->order('id desc')
->limit(100)
->select();
$invoice = InvoiceLog::with(['invoice'])->where('id',$id)->find();
// 处理文件路径转换为完整URL用于显示
if (!empty($invoice['document'])) {
$currentDomain = $_SERVER['HTTP_HOST'];
$invoice['document'] = 'http://'.$currentDomain.'/public/'.$invoice['document'];
}
$this->assign([
'invoice' => $invoice,
'orders' => $orders,
]);
return $this->fetch();
}
/**
* 删除
*/
public function del()
{
if ($this->request->isAjax() && $this->request->isPost()) {
$id = $this->request->post('id', 0);
if (empty($id)) {
$this->_error('参数错误');
}
// 检查数据是否存在
$invoice = Db::name('invoice_log')->where('id', $id)->find();
if (empty($invoice)) {
$this->_error('数据不存在');
}
// 删除数据
$result = Db::name('invoice_log')->where('id', $id)->delete();
if ($result) {
$this->_success('删除成功');
} else {
$this->_error('删除失败');
}
} else {
$this->_error('请求方式错误');
}
}
/**
* 上传发票文件
*/
public function upload()
{
if ($this->request->isPost()) {
$file = $this->request->file('file');
if (empty($file)) {
$this->_error('请选择文件');
}
// 获取文件信息
$fileInfo = $file->getInfo();
$fileName = $fileInfo['name'] ?? '';
// 验证文件类型PDF- 从文件名获取扩展名(最可靠的方法)
$ext = '';
if (!empty($fileName)) {
// 使用pathinfo获取扩展名支持中文文件名
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
}
// 如果从文件名获取失败尝试从MIME类型判断
if (empty($ext) && isset($fileInfo['type'])) {
$mimeType = strtolower($fileInfo['type']);
if (strpos($mimeType, 'pdf') !== false || $mimeType === 'application/pdf') {
$ext = 'pdf';
}
}
// 验证扩展名
if (empty($ext) || $ext !== 'pdf') {
$this->_error('只支持PDF格式文件当前文件' . $fileName . ',扩展名:' . ($ext ?: '未知'));
}
// 设置验证规则
$file->validate(['ext' => 'pdf']);
// 上传文件
$uploadPath = rtrim(ROOT_PATH, '/\\') . '/public/uploads/invoice';
$info = $file->move($uploadPath);
if ($info) {
// 返回文件路径(相对路径)
$filePath = 'uploads/invoice/' . $info->getSaveName();
$this->_success('上传成功', ['path' => $filePath]);
} else {
$errorMsg = $file->getError();
$this->_error('上传失败:' . ($errorMsg ?: '未知错误'));
}
} else {
$this->_error('请求方式错误');
}
}
}

View File

@@ -0,0 +1,266 @@
<?php
namespace app\admin\controller;
use app\admin\model\Staff;
use app\admin\server\MomentsServer;
use think\Db;
class Moments extends AdminBase
{
public function lists()
{
if ($this->request->isAjax()) {
$get = $this->request->get();
$list = MomentsServer::getMoments();
// 格式化数据为前端期望的格式
$data = [
'count' => count($list),
'lists' => $list
];
$this->_success('', $data);
}
return $this->fetch();
}
public function add()
{
if ($this->request->isAjax() && $this->request->isPost()) {
// 处理表单提交
$post = $this->request->post();
// 表单验证
if (empty($post['title'])) {
$this->_error('请输入标题');
}
if (empty($post['sid'])) {
$this->_error('请选择员工');
}
if (empty($post['goods_id'])) {
$this->_error('请选择关联产品');
}
// 处理图片字段(可能是数组格式 goods_image[]
$goods_images = [];
if (isset($post['goods_image'])) {
if (is_array($post['goods_image'])) {
// 数组格式,过滤空值
$goods_images = array_filter($post['goods_image']);
} else if (!empty($post['goods_image'])) {
// 字符串格式(逗号分隔),转换为数组
$goods_images = explode(',', $post['goods_image']);
$goods_images = array_filter($goods_images);
}
}
// 验证图片
if (empty($goods_images)) {
$this->_error('请选择图片');
}
// 将图片数组转换为逗号分隔的字符串
$post['goods_image'] = implode(',', $goods_images);
// 添加创建时间
$post['create_time'] = date('Y-m-d H:i:s',time());
$post['page_views'] = 0;
$post['like'] = 0;
$post['collection'] = 0;
// 保存数据
$result = Db::name('moments')->insert($post);
if ($result) {
$this->_success('添加成功');
} else {
$this->_error('添加失败');
}
}
//查询员工信息
$staff = Db::name('staff')->where('onwork',1)->field('id,name')->select();
//查询商品信息
$goods = Db::name('goods')
->where('status',1)
->order('code desc')
->field('id,name')->select();
$this->assign([
'staff' => $staff,
'goods' => $goods,
]);
return $this->fetch();
}
/**
* 查看详情
*/
public function info()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
$this->_error('参数错误');
}
$moment = Db::name('moments')->where('id', $id)->find();
if (empty($moment)) {
$this->_error('数据不存在');
}
// 获取关联数据
$staff = null;
if (!empty($moment['sid'])) {
$staff = Db::name('staff')->where('id', $moment['sid'])->field('id,name')->find();
}
$goods = null;
if (!empty($moment['goods_id'])) {
$goods = Db::name('goods')->where('id', $moment['goods_id'])->field('id,name')->find();
}
// 处理图片转换为完整URL
$images = [];
if (!empty($moment['goods_image'])) {
$imageList = explode(',', $moment['goods_image']);
foreach ($imageList as $img) {
$img = trim($img);
if (!empty($img)) {
$images[] = \app\common\server\UrlServer::getFileUrl($img);
}
}
}
$this->assign([
'moment' => $moment,
'staff' => $staff,
'goods' => $goods,
'images' => $images,
]);
return $this->fetch();
}
/**
* 编辑
*/
public function edit()
{
$id = $this->request->param('id', 0);
if (empty($id)) {
$this->_error('参数错误');
}
if ($this->request->isAjax() && $this->request->isPost()) {
// 处理表单提交
$post = $this->request->post();
// 表单验证
if (empty($post['title'])) {
$this->_error('请输入标题');
}
if (empty($post['sid'])) {
$this->_error('请选择员工');
}
if (empty($post['goods_id'])) {
$this->_error('请选择关联产品');
}
// 处理图片字段(可能是数组格式 goods_image[]
$goods_images = [];
if (isset($post['goods_image'])) {
if (is_array($post['goods_image'])) {
// 数组格式,过滤空值
$goods_images = array_filter($post['goods_image']);
} else if (!empty($post['goods_image'])) {
// 字符串格式(逗号分隔),转换为数组
$goods_images = explode(',', $post['goods_image']);
$goods_images = array_filter($goods_images);
}
}
// 验证图片
if (empty($goods_images)) {
$this->_error('请选择图片');
}
// 将图片数组转换为逗号分隔的字符串
$post['goods_image'] = implode(',', $goods_images);
// 更新数据
$result = Db::name('moments')->where('id', $id)->update($post);
if ($result !== false) {
$this->_success('编辑成功');
} else {
$this->_error('编辑失败');
}
}
// 获取当前数据
$moment = Db::name('moments')->where('id', $id)->find();
if (empty($moment)) {
$this->_error('数据不存在');
}
// 处理图片转换为完整URL用于显示同时保存原始路径用于提交
$images = [];
$imagePaths = [];
if (!empty($moment['goods_image'])) {
$imageList = explode(',', $moment['goods_image']);
foreach ($imageList as $img) {
$img = trim($img);
if (!empty($img)) {
$imagePaths[] = $img; // 保存原始路径用于提交
$images[] = \app\common\server\UrlServer::getFileUrl($img); // 转换为完整URL用于显示
}
}
}
//查询员工信息
$staff = Db::name('staff')->where('onwork',1)->field('id,name')->select();
//查询商品信息
$goodsList = Db::name('goods')
->where('status',1)
->order('code desc')
->field('id,name')->select();
$this->assign([
'moment' => $moment,
'staff' => $staff,
'goods' => $goodsList,
'images' => $images,
'imagePaths' => $imagePaths, // 传递原始路径
]);
return $this->fetch();
}
/**
* 删除
*/
public function del()
{
if ($this->request->isAjax() && $this->request->isPost()) {
$id = $this->request->post('id', 0);
if (empty($id)) {
$this->_error('参数错误');
}
// 检查数据是否存在
$moment = Db::name('moments')->where('id', $id)->find();
if (empty($moment)) {
$this->_error('数据不存在');
}
// 删除数据
$result = Db::name('moments')->where('id', $id)->delete();
if ($result) {
$this->_success('删除成功');
} else {
$this->_error('删除失败');
}
} else {
$this->_error('请求方式错误');
}
}
}

View File

@@ -100,26 +100,42 @@ class Order extends AdminBase
*/
public function del()
{
if ($this->request->isAjax()) {
dump(123);die;
// $post = $this->request->post('');
// $order=Db::name('order')->where('id',$post['order_id'])->find();//查询订单信息
// if($this->admin_id!=6){
// $this->_error('无权限删除');
// }else{
// $orderdel=Db::name('order_exe')->where('order_sn',$order['order_sn'])->find();
// if($orderdel){
// $this->_error('无法删除,存在主订单');
// }else{
// $del=Db::name('order')->where('id',$post['order_id'])->delete();
// $this->_success('删除成功');
// }
// }
$id = $this->request->get('id');
$order = Db::name('order')->find($id);//查询订单信息
if ($order == null){
$this->_error('订单不存在');
}
$del = Db::name('order')->where('id',$id)->update(['del' => 1]);
if($del){
$this->_success('删除成功');
}else{
$this->_error('删除失败');
}
}
}
/**
* User: 恢复已删除订单
* Desc: 将 del 从 1 恢复为 0
*/
public function recover()
{
if ($this->request->isAjax()) {
$id = $this->request->get('id');
$order = Db::name('order')->find($id);// 查询订单信息
if ($order == null){
$this->_error('订单不存在');
}
if ($order['del'] == 0) {
$this->_error('订单未删除,无需恢复');
}
$res = Db::name('order')->where('id',$id)->update(['del' => 0]);
if ($res) {
$this->_success('恢复成功');
} else {
$this->_error('恢复失败');
}
}
}
@@ -268,7 +284,7 @@ class Order extends AdminBase
$this->_success('修改订单成功', OrderLogic::edit($post));
}
$this->assign('info', json_encode(OrderLogic::orderinfoid($id), JSON_UNESCAPED_UNICODE));
$this->assign('info', json_encode(OrderLogic::orderinfoid($id), JSON_UNESCAPED_UNICODE));
$brand_lists=Db::name('orderchannel')->select();
$this->assign('brand_lists', json_encode($brand_lists, JSON_UNESCAPED_UNICODE));
$this->assign('category_lists', json_encode(StaffLogic::getAllTree(), JSON_UNESCAPED_UNICODE));

View File

@@ -660,6 +660,13 @@ public function group_del(){
return $this->fetch();
}
// 员工工资导出
public function exportWages()
{
$get = $this->request->get();
$this->_success('', StaffLogic::wagesExport($get));
}
//删除员工的工资
public function wagesdel(){
$post = $this->request->post('id'); //获取到传递过来的参数

View File

@@ -218,10 +218,37 @@ class AdLogic
*/
public static function infoPosition($client)
{
$position_list = Db::name('ad_position')
->where(['client' => $client, 'status' => 1, 'del' => 0])
->group('name')
->column('id,name', 'id');
$position_list = [];
// 使用错误抑制,避免 unserialize 警告影响页面
$originalErrorReporting = error_reporting(0);
try {
// 使用原始 SQL 查询,完全绕过 ThinkPHP 的类型转换
$prefix = config('database.prefix');
$tableName = $prefix . 'ad_position';
$sql = "SELECT DISTINCT `id`, `name` FROM `{$tableName}` WHERE `client` = " . intval($client) . " AND `status` = 1 AND `del` = 0 GROUP BY `name`";
$result = Db::query($sql);
if (!empty($result) && is_array($result)) {
foreach ($result as $row) {
if (isset($row['id']) && isset($row['name'])) {
$position_list[$row['id']] = $row['name'];
}
}
}
} catch (\Exception $e) {
// 忽略所有异常
} catch (\Error $e) {
// 忽略所有错误
} catch (\Throwable $e) {
// 捕获所有可抛出的对象
} finally {
// 恢复错误报告级别
error_reporting($originalErrorReporting);
}
asort($position_list);
return $position_list;
}

View File

@@ -57,14 +57,11 @@ class ControlLogic{
$item['add']=$order_info['add'];
$order=Db::name('order')->where('order_sn',$order_info['order_sn'])->find();
$item['adderss']=$order['address'];
$goods=Db::name('goods')->where('id',$order['goods_id'])->find();
$item['goods_name']=$goods['name'];
if ($order != null){
$item['adderss']=$order['address'] ?? "";
$goods=Db::name('goods')->where('id',$order['goods_id'])->find();
$item['goods_name']=$goods['name'] ?? "";
}
}
return ['count'=>$count , 'lists'=>$lists];

View File

@@ -47,6 +47,7 @@ class GoodsCategoryLogic
'level' => $level + 1,
'is_recommend' => $post['pid'] == 0 ? $is_recommend : 0,
'remark' => $post['remark'],
'type' => $post['type'], //1:普通分类 2:推荐分类
'create_time' => time(),
'update_time' => time(),
];
@@ -77,6 +78,7 @@ class GoodsCategoryLogic
'pid' => $post['pid'],
'is_recommend' => $post['pid'] == 0 ? $is_recommend : 0,
'remark' => $post['remark'],
'type' => $post['type'], //1:普通分类 2:推荐分类
'update_time' => time(),
];
@@ -128,7 +130,7 @@ class GoodsCategoryLogic
$lists = Db::name('goods_category')
->where(['del' => 0])
->order('id asc')
->column('id,name,pid,is_recommend,is_show,level,image,sort');
->column('id,name,pid,is_recommend,is_show,level,image,sort,type');
foreach ($lists as $k => $item){
$lists[$k]['image'] = UrlServer::getFileUrl($item['image']);
}

View File

@@ -291,7 +291,8 @@ class GoodsLogic
'give_integral_type' => $give_integral_type,
'give_integral' => $give_integral,
'spec_type' => $post['spec_type'],
'create_time' => $time
'create_time' => $time,
'type' => $post['type']
];
$goods_id = Db::name('goods')->insertGetId($data);
@@ -481,7 +482,8 @@ class GoodsLogic
'give_integral_type' => $give_integral_type,
'give_integral' => $give_integral,
'spec_type' => $post['spec_type'],
'update_time' => $time
'update_time' => $time,
'type' => $post['type'],
];
Db::name('goods')

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,9 @@ class OrderautoLogic{
public static function lists($get){
$where = [];
//判断已经支付的订单
$where[] = ['pay_status', '=', 1];
// $where[] = ['pay_status', '=', 1];
$where[] = ['refund_status', '=', 0];
$where[] = ['del', '=', 0];
if(isset($get['type']) && $get['type'] != ''){
$where[] = ['static','=',$get['type']];
}
@@ -38,10 +40,8 @@ class OrderautoLogic{
$where[] = ['status','=',$get['status']];
}
// 执行状态查询
if(isset($get['admin']) && $get['admin']!= ''){
$where[] = ['admin_id','=',$get['admin']];
}
// 如果订单的admin_id不等于空不需要限制支付状态如果admin_id等于空只查询已支付的订单
$where[] = ['', 'exp', Db::raw("(admin_id IS NOT NULL AND admin_id != '') OR pay_status = 1")];
// 订单的来源查询
if(isset($get['pid']) && $get['pid']!= ''){
$where[] = ['channel_id','=',$get['pid']];

View File

@@ -625,6 +625,47 @@ class StaffLogic{
return Db::name('staff')->where('id',$post['id'])->update(['lng'=>$post['store_longitude'],'lat'=>$post['store_latitude']]);
}
// 员工工资导出数据(基于 wages 列表结果,避免重复重算导致超时)
public static function wagesExport($get)
{
// 导出时最多导出 5000 条,且沿用 wages 的统计逻辑
$get['page'] = 1;
$get['limit'] = isset($get['limit']) && (int)$get['limit'] > 0 ? (int)$get['limit'] : 5000;
$data = self::wages($get);
$lists = $data['lists'] ?? [];
// 组装导出数据
$exportTitle = ['员工姓名', '员工电话', '服务总订单', '上午订单', '下午订单', '财务报销', '上午加时', '下午加时', '请假天数', '出勤天数', '罚款金额', '异常订单', '年卡订单', '物料金额'];
$exportData = [];
foreach ($lists as $row) {
$exportData[] = [
$row['name'] ?? '',
$row['mobile'] ?? '',
$row['total_orders'] ?? 0,
$row['sw_orders'] ?? 0,
$row['xw_orders'] ?? 0,
$row['finance'] ?? 0,
$row['sw_addtimes'] ?? 0,
$row['xw_addtimes'] ?? 0,
$row['leaves'] ?? 0,
$row['attendance'] ?? 0,
$row['fine'] ?? 0,
$row['abnormal'] ?? 0,
$row['annual'] ?? 0,
$row['erp_staff'] ?? 0,
];
}
$exportExt = 'xls';
return [
'exportTitle' => $exportTitle,
'exportData' => $exportData,
'exportExt' => $exportExt,
'exportName' => '员工工资-' . date('Y-m-d H:i:s'),
];
}
//获取员工积分记录
public static function points($get){
$where=[];

View File

@@ -0,0 +1,19 @@
<?php
namespace app\admin\model;
use think\Model;
class Invoice extends Model
{
protected $name = 'invoice';
public function user()
{
return $this->belongsTo(User::class, 'uid', 'id');
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace app\admin\model;
use think\Model;
class InvoiceLog extends Model
{
protected $name = 'invoice_log';
public function invoice()
{
return $this->belongsTo(Invoice::class, 'invoice_id', 'id');
}
public function order()
{
return $this->belongsTo(Order::class, 'oid', 'id');
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace app\admin\model;
use app\common\model\Goods;
use think\Model;
class Moments extends Model
{
protected $name = 'moments';
/**
* 关联员工belongsTomoments 属于 staffmoments.sid = staff.id
*/
public function getStaff()
{
return $this->belongsTo(Staff::class, 'sid', 'id');
}
/**
* 关联订单belongsTomoments 属于 ordermoments.oid = order.id
*/
public function getOrder()
{
return $this->belongsTo(Order::class, 'oid', 'id');
}
//关联产品
public function getProduct()
{
return $this->belongsTo(Goods::class, 'goods_id', 'id');
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\admin\model;
use think\Model;
class Order extends Model
{
protected $name = 'order';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\admin\model;
use think\Model;
class OrderGoods extends Model
{
protected $name = 'order_service';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\admin\model;
use think\Model;
class OrderRecharge extends Model
{
protected $name = 'recharge_order';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\admin\model;
use think\Model;
class OrderYearcard extends Model
{
protected $name = 'order_yearcard';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\admin\model;
use think\Model;
class Staff extends Model
{
protected $name = 'staff';
}

View File

@@ -0,0 +1,91 @@
<?php
namespace app\admin\server;
use app\admin\model\Moments;
use app\common\server\UrlServer;
use think\Db;
class MomentsServer
{
public static function getMoments()
{
try {
$data = Moments::with([
'getStaff'=>function ($query) {
$query->field('id,name');
},
'getOrder'=>function ($query) {
$query->field('id,order_sn');
},
'getProduct'=>function ($query) {
$query->field('id,name');
}
])
->order('id desc')
->select();
// 如果查询结果为空,尝试不使用关联查询
if (empty($data)) {
$data = Moments::order('id desc')->select();
}
//处理数据
$result = [];
if ($data != null){
foreach ($data as $value) {
// 从 goods_image 字段中取第一张图片作为封面
$img = '';
if (!empty($value['goods_image'])) {
$images = explode(',', $value['goods_image']);
$img = trim($images[0]);
$img = UrlServer::getFileUrl($img);
}
$result[] = [
'id' => $value['id'] ?? 0,
'title' => $value['title'] ?? '',
'order_sn' => $value['order_id'],
'img' => $img,
'create_time' => $value['create_time'] ?? '',
'staff_name' => $value['getStaff']['name'] ?? null,
'goods_name' => $value['getProduct']['name'] ?? null,
'collection' => $value['collection'] ?? 0,
'page_views' => $value['page_views'] ?? 0,
'like' => $value['like'] ?? 0,
'state' => $value['state'] ?? 1,
];
}
}
return $result;
} catch (\Exception $e) {
// 如果关联查询出错,使用简单查询
$data = Moments::order('id desc')->select();
$result = [];
foreach ($data as $value) {
// 从 goods_image 字段中取第一张图片作为封面
$img = '';
if (!empty($value['goods_image'])) {
$images = explode(',', $value['goods_image']);
$firstImage = trim($images[0]);
// 转换为完整的图片URL
$img = UrlServer::getFileUrl($firstImage);
}
$result[] = [
'id' => $value['id'],
'title' => $value['title'],
'order_sn' => $value['order_id'] ?? '',
'img' => $img,
'create_time' => $value['create_time'],
'staff_name' => null,
'goods_name' => null,
'collection' => $value['collection'] ?? 0,
'page_views' => $value['page_views'] ?? 0,
'like' => $value['like'] ?? 0,
'state' => $value['state'] ?? 1,
];
}
return $result;
}
}
}

View File

@@ -44,6 +44,7 @@ class Goods extends Validate
'region_ratio' => 'lt:100',
'shareholder_ratio' => 'lt:100',
'give_integral_ratio' => 'egt:0',
'type' => 'require',
];
protected $message = [
@@ -69,6 +70,7 @@ class Goods extends Validate
'region_ratio.lt' => '区域分红比例不能超过100',
'shareholder_ratio.lt' => '股东分红比例不能超过100',
'give_integral_ratio.egt' => '赠送积分比例须大于或等于0',
'type.require' => '请选择商品类型',
];
/**

View File

@@ -30,6 +30,7 @@ class GoodsCategory extends Validate
'name' => 'require|checkName',
'pid' => 'addPid|editPid',
'image' => 'require',
'type' => 'require',
];
protected $message = [
@@ -37,6 +38,7 @@ class GoodsCategory extends Validate
'name.require' => '分类名称不能为空!',
'name.unique' => '分类名称已存在',
'image.require' => '请上传分类图标',
'type.require' => '请选择分类类型'
];
protected function sceneAdd()
{

View File

@@ -30,6 +30,8 @@ class GoodsOneSpec extends Validate
'one_stock' => 'require|integer',
'one_volume' => 'egt:0',
'one_weight' => 'egt:0',
'type' => 'require',
'points' => 'egt:0',
];
protected $message = [
@@ -45,6 +47,8 @@ class GoodsOneSpec extends Validate
'one_cost_price.egt' => '成本价必须大于或等于0.01',
'one_stock.require' => '请输入库存',
'one_stock.integer' => '库存必须为整型',
'type.require' => '请选择规格类型',
'points.egt' => '积分必须为正整数',
];

View File

@@ -0,0 +1,673 @@
{layout name="layout1" /}
<style>
.dashboard-container {
padding: 15px;
}
.kpi-card {
background: #fff;
padding: 20px;
border-radius: 4px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.kpi-title {
font-size: 14px;
color: #666;
margin-bottom: 10px;
}
.kpi-value {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.kpi-compare {
font-size: 12px;
color: #999;
}
.chart-card {
background: #fff;
padding: 20px;
border-radius: 4px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.chart-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.chart-container {
height: 300px;
}
.chart-container-large {
height: 400px;
}
.progress-item {
margin-bottom: 15px;
}
.progress-label {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
font-size: 14px;
}
.progress-bar-container {
height: 20px;
background: #f0f0f0;
border-radius: 10px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #4A70F4 0%, #6DD047 100%);
transition: width 0.3s;
}
.table-card {
background: #fff;
padding: 20px;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
overflow: hidden;
}
.date-filter {
display: flex;
align-items: center;
gap: 10px;
}
/* 优化表格样式 */
.table-card .layui-table {
margin-top: 0;
border: none;
}
.table-card .layui-table thead tr {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.table-card .layui-table thead th {
color: #fff;
font-weight: 600;
text-align: center;
padding: 15px 10px;
border: none;
font-size: 14px;
}
.table-card .layui-table tbody tr {
transition: all 0.3s ease;
border-bottom: 1px solid #f0f0f0;
}
.table-card .layui-table tbody tr:hover {
background-color: #f8f9ff;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.table-card .layui-table tbody td {
text-align: center;
padding: 12px 10px;
border: none;
color: #333;
font-size: 13px;
}
.table-card .layui-table tbody td:first-child {
font-weight: 600;
color: #4A70F4;
background-color: #f8f9ff;
}
.table-card .layui-table tbody td.empty-data {
color: #999;
font-style: italic;
}
.table-card .chart-title {
display: flex;
align-items: center;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
margin-bottom: 20px;
}
.table-card .chart-title::before {
content: '';
display: inline-block;
width: 4px;
height: 18px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin-right: 10px;
border-radius: 2px;
}
</style>
<div class="layui-fluid dashboard-container">
<!-- 顶部KPI指标 -->
<div class="layui-row layui-col-space15">
<div class="layui-col-md3">
<div class="kpi-card">
<div class="kpi-title">月销售总额</div>
<div class="kpi-value" id="monthly-sales">--</div>
<div class="kpi-compare">同比: <span id="sales-compare">--</span></div>
</div>
</div>
<div class="layui-col-md3">
<div class="kpi-card">
<div class="kpi-title">月订单量</div>
<div class="kpi-value" id="monthly-orders">--</div>
<div class="kpi-compare">同比: <span id="orders-compare">--</span></div>
</div>
</div>
<div class="layui-col-md3">
<div class="kpi-card">
<div class="kpi-title">月客户数</div>
<div class="kpi-value" id="monthly-customers">--</div>
<div class="kpi-compare">同比: <span id="customers-compare">--</span></div>
</div>
</div>
<div class="layui-col-md3">
<div class="kpi-card">
<div class="kpi-title">日期筛选</div>
<div class="date-filter">
<input type="text" class="layui-input" id="date-filter" placeholder="年月日(可筛选)">
</div>
</div>
</div>
</div>
<!-- 第一行图表 -->
<div class="layui-row layui-col-space15">
<!-- 月客户分析 -->
<div class="layui-col-md4">
<div class="chart-card">
<div class="chart-title">月客户分析</div>
<div class="chart-container" id="customer-analysis-chart"></div>
</div>
</div>
<!-- 月销售情况 -->
<div class="layui-col-md4">
<div class="chart-card">
<div class="chart-title">月销售情况</div>
<div class="chart-container" id="sales-situation-chart"></div>
</div>
</div>
<!-- 当月客户渠道分析 -->
<div class="layui-col-md4">
<div class="chart-card">
<div class="chart-title">当月客户渠道分析</div>
<div class="chart-container" id="channel-analysis-chart"></div>
</div>
</div>
</div>
<!-- 第二行:目标完成率 -->
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="chart-card">
<div class="chart-title">当月目标完成率</div>
<div style="margin-bottom: 20px;">
<div style="font-size: 14px; color: #666; margin-bottom: 10px;">
本月销售目标:
<span style="color: #4A70F4; font-weight: bold;" id="monthly-target-text">--</span>
</div>
<div class="progress-item">
<div class="progress-label">
<span>总体完成率</span>
<span style="color: #4A70F4; font-weight: bold;" id="overall-completion-text">--</span>
</div>
<div class="progress-bar-container">
<div class="progress-bar" id="overall-completion-bar" style="width: 0%;"></div>
</div>
</div>
</div>
<div style="margin-top: 30px;">
<div class="chart-title" style="font-size: 14px; margin-bottom: 15px;">客服目标完成率(N个客服的目标陈列)</div>
<div id="staff-progress-list">
<!-- 客服进度条列表JS 根据数据库数据渲染) -->
</div>
</div>
</div>
</div>
</div>
<!-- 第三行:趋势图和区域图 -->
<div class="layui-row layui-col-space15">
<!-- 每日销售趋势 -->
<div class="layui-col-md6">
<div class="chart-card">
<div class="chart-title">每日销售趋势</div>
<div id="trend-compare-text" style="text-align: right; margin-bottom: 10px; color: #f56c6c; font-size: 14px;">
--
</div>
<div class="chart-container-large" id="daily-sales-trend-chart"></div>
</div>
</div>
<!-- 区域月销售额 -->
<div class="layui-col-md6">
<div class="chart-card">
<div class="chart-title">区域月销售额</div>
<div class="chart-container-large" id="regional-sales-chart"></div>
</div>
</div>
</div>
<!-- 第四行:数据表格 -->
<div class="layui-row layui-col-space15">
<!-- 渠道月销售详情 -->
<div class="layui-col-md12">
<div class="table-card">
<div class="chart-title">渠道月销售详情</div>
<table class="layui-table" lay-size="sm">
<thead>
<tr>
<th>渠道</th>
<th>美团</th>
<th>公众号</th>
<th>抖音</th>
<th>员工</th>
<th>异业</th>
<th>渠道</th>
</tr>
</thead>
<tbody id="channel-month-table-body">
<!-- JS 动态渲染渠道月销售详情 -->
</tbody>
</table>
</div>
</div>
</div>
<!-- 第五行:区域月销售详情 -->
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="table-card">
<div class="chart-title">区域月销售详情</div>
<table class="layui-table" lay-size="sm">
<thead>
<tr>
<th>服务类型</th>
<th>南明区</th>
<th>云岩区</th>
<th>白云区</th>
<th>乌当区</th>
<th>花溪区</th>
<th>龙里</th>
</tr>
</thead>
<tbody id="region-month-table-body">
<!-- JS 动态渲染区域月销售详情 -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<script>
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index', 'laydate'], function(){
var $ = layui.$
,laydate = layui.laydate;
// 统计数据(从后端传递)
var statisticsData = {
monthlySales: parseFloat('{$monthlySales|default=0}') || 0,
monthlyOrders: parseInt('{$monthlyOrders|default=0}') || 0,
monthlyCustomers: parseInt('{$monthlyCustomers|default=0}') || 0,
salesCompare: parseFloat('{$salesCompare|default=0}') || 0,
ordersCompare: parseFloat('{$ordersCompare|default=0}') || 0,
customersCompare: parseFloat('{$customersCompare|default=0}') || 0,
oldCustomers: parseInt('{$oldCustomers|default=0}') || 0,
newCustomers: parseInt('{$newCustomers|default=0}') || 0
};
// 仪表盘其它统计数据(目标、客服进度、渠道、趋势、区域等)
// 这里直接输出后端已经 json_encode 好的字符串,避免模板语法冲突
var dashboardData = {$dashboardData|raw} || {};
// 渲染统计数据
renderStatistics();
// 日期选择器
laydate.render({
elem: '#date-filter',
type: 'date',
format: 'yyyy-MM-dd'
});
// 初始化所有图表(使用数据库数据渲染)
initCharts();
// 渲染目标完成率和客服进度条
renderTargetsAndStaff();
// 渲染渠道与区域明细表格
renderChannelAndRegionTables();
// 渲染统计数据
function renderStatistics() {
// 格式化金额(保留两位小数,添加千分位)
function formatMoney(amount) {
if (!amount) return '0.00';
return parseFloat(amount).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
// 格式化同比显示
function formatCompare(compare) {
if (compare > 0) {
return '<span style="color: #52c41a;">↑ ' + Math.abs(compare).toFixed(2) + '%</span>';
} else if (compare < 0) {
return '<span style="color: #ff4d4f;">↓ ' + Math.abs(compare).toFixed(2) + '%</span>';
} else {
return '<span style="color: #999;">持平</span>';
}
}
// 渲染月销售总额
$('#monthly-sales').text('¥' + formatMoney(statisticsData.monthlySales));
$('#sales-compare').html(formatCompare(statisticsData.salesCompare));
// 渲染月订单量
$('#monthly-orders').text(statisticsData.monthlyOrders);
$('#orders-compare').html(formatCompare(statisticsData.ordersCompare));
// 渲染月客户数
$('#monthly-customers').text(statisticsData.monthlyCustomers);
$('#customers-compare').html(formatCompare(statisticsData.customersCompare));
}
// 渲染销售目标、总体完成率和客服进度条
function renderTargetsAndStaff() {
if (!dashboardData) {
return;
}
// 销售目标与总体完成率
var target = dashboardData.monthlyTarget || 0;
var completion = dashboardData.overallCompletion || 0;
$('#monthly-target-text').text(target > 0 ? (target / 10000).toFixed(2) + ' 万' : '--');
$('#overall-completion-text').text(completion.toFixed ? completion.toFixed(2) + '%' : (completion + '%'));
$('#overall-completion-bar').css('width', (completion > 100 ? 100 : completion) + '%');
// 客服进度条
var list = $('#staff-progress-list');
list.empty();
if (dashboardData.staffProgress && dashboardData.staffProgress.length) {
dashboardData.staffProgress.forEach(function (item) {
var percentText = (item.percent || 0).toFixed(2) + '%';
var yoy = item.yoy || 0;
var yoyHtml;
if (yoy > 0) {
yoyHtml = '同比: <span style="color:#52c41a;">↑ ' + yoy.toFixed(2) + '%</span>';
} else if (yoy < 0) {
yoyHtml = '同比: <span style="color:#ff4d4f;">↓ ' + Math.abs(yoy).toFixed(2) + '%</span>';
} else {
yoyHtml = '同比: <span style="color:#999;">持平</span>';
}
var html = ''
+ '<div class="progress-item">'
+ ' <div class="progress-label">'
+ ' <span>' + (item.name || '未分配') + '</span>'
+ ' <span>销售额: ' + (item.sales || 0) + ' | 百分比: ' + percentText + ' | ' + yoyHtml + '</span>'
+ ' </div>'
+ ' <div class="progress-bar-container">'
+ ' <div class="progress-bar" style="width:' + (item.percent || 0) + '%;"></div>'
+ ' </div>'
+ '</div>';
list.append(html);
});
} else {
list.append('<div style="color:#999;font-size:12px;">暂无客服统计数据</div>');
}
}
// 渠道&区域月销售明细表格渲染(只处理“总额”这一行)
function renderChannelAndRegionTables() {
if (!dashboardData) {
return;
}
// ===== 渠道月销售详情(总额 / 年卡 / 次卡 / 单次 / 其它) =====
var channelBody = $('#channel-month-table-body');
channelBody.empty();
var channelHeaders = ['美团','公众号','抖音','员工','异业','渠道'];
var rowLabels = [
{key: 'total', text: '总额'},
{key: 'year', text: '年卡'},
{key: 'times', text: '次卡'},
{key: 'single',text: '单次服务'},
{key: 'other', text: '其他服务'}
];
var keyToCol = ['meituan','gzh','douyin','staff','yeye','other'];
rowLabels.forEach(function (row) {
var data = (dashboardData.channelDetail && dashboardData.channelDetail[row.key]) || {};
var tr = '<tr><td>' + row.text + '</td>';
keyToCol.forEach(function (colKey) {
var v = parseFloat(data[colKey] || 0).toFixed(2);
tr += '<td>' + v + '</td>';
});
tr += '</tr>';
channelBody.append(tr);
});
// ===== 区域月销售详情(总额 / 年卡 / 次卡 / 单次 / 其它) =====
var regionBody = $('#region-month-table-body');
regionBody.empty();
if (dashboardData.regions && dashboardData.regions.length) {
var regionNames = dashboardData.regions;
rowLabels.forEach(function (row) {
var rowData = (dashboardData.regionDetail && dashboardData.regionDetail[row.key]) || {};
var tr2 = '<tr><td>' + row.text + '</td>';
regionNames.forEach(function (rName) {
var val = parseFloat(rowData[rName] || 0).toFixed(2);
tr2 += '<td>' + val + '</td>';
});
tr2 += '</tr>';
regionBody.append(tr2);
});
}
}
function initCharts() {
// 月客户分析 - 饼图
var customerChart = echarts.init(document.getElementById('customer-analysis-chart'));
customerChart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [{
name: '客户分析',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}: {d}%'
},
data: [
{value: statisticsData.oldCustomers, name: '老客', itemStyle: {color: '#FFB6C1'}},
{value: statisticsData.newCustomers, name: '新客', itemStyle: {color: '#FF69B4'}}
]
}]
});
// 月销售情况 - 饼图(使用数据库汇总结果)
var salesChart = echarts.init(document.getElementById('sales-situation-chart'));
salesChart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
bottom: '5%',
left: 'center'
},
series: [{
name: '销售情况',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}: {c}'
},
data: (dashboardData.salesSituation && dashboardData.salesSituation.length)
? dashboardData.salesSituation
: [
{value: 0, name: '单次服务', itemStyle: {color: '#F6A23F'}},
{value: 0, name: '年卡销量', itemStyle: {color: '#4A70F4'}},
{value: 0, name: '次卡销量', itemStyle: {color: '#6DD047'}},
{value: 0, name: '其它服务', itemStyle: {color: '#87CEEB'}}
]
}]
});
// 当月客户渠道分析 - 环形图(使用数据库统计的渠道订单数)
var channelChart = echarts.init(document.getElementById('channel-analysis-chart'));
channelChart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [{
name: '客户渠道',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}: {d}%'
},
data: (dashboardData.channelPie && dashboardData.channelPie.length)
? dashboardData.channelPie
: []
}]
});
// 每日销售趋势 - 折线图(使用数据库按天汇总的数据)
var trendChart = echarts.init(document.getElementById('daily-sales-trend-chart'));
trendChart.setOption({
tooltip: {
trigger: 'axis'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dashboardData.trendDates || []
},
yAxis: {
type: 'value',
name: '销售额',
axisLabel: {
formatter: '{value}K'
}
},
series: [{
name: '销售额',
type: 'line',
smooth: true,
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(74, 112, 244, 0.3)'
}, {
offset: 1, color: 'rgba(74, 112, 244, 0.1)'
}]
}
},
itemStyle: {
color: '#4A70F4'
},
data: dashboardData.trendValues || []
}]
});
// 渲染“上月同比”文字
var trendCompare = dashboardData.trendCompare || 0;
var trendTextDom = $('#trend-compare-text');
if (trendCompare > 0) {
trendTextDom.css('color', '#52c41a').html('▲ ' + trendCompare.toFixed(2) + '% 上月同比');
} else if (trendCompare < 0) {
trendTextDom.css('color', '#f56c6c').html('▼ ' + Math.abs(trendCompare).toFixed(2) + '% 上月同比');
} else {
trendTextDom.css('color', '#999').html('持平 上月同比');
}
// 区域月销售额 - 柱状图(根据地址简单按区域统计)
var regionalChart = echarts.init(document.getElementById('regional-sales-chart'));
regionalChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: dashboardData.regions || []
},
yAxis: {
type: 'value',
name: '销量'
},
series: [{
name: '销量',
type: 'bar',
itemStyle: {
color: '#4A70F4'
},
data: dashboardData.regionTotals || []
}]
});
// 响应式调整
window.addEventListener('resize', function() {
customerChart.resize();
salesChart.resize();
channelChart.resize();
trendChart.resize();
regionalChart.resize();
});
}
});
</script>

View File

@@ -0,0 +1,645 @@
{layout name="layout1" /}
<style>
.map-container {
padding: 15px;
min-height: calc(100vh - 60px);
}
.map-header {
background: #fff;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
display: flex;
align-items: center;
justify-content: space-between;
}
.map-header-left {
display: flex;
align-items: center;
gap: 15px;
}
.map-header-title {
font-size: 18px;
font-weight: bold;
color: #333;
}
.map-header-stats {
display: flex;
gap: 20px;
align-items: center;
}
.stat-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 15px;
background: #f8f9ff;
border-radius: 6px;
}
.stat-label {
font-size: 13px;
color: #666;
}
.stat-value {
font-size: 16px;
font-weight: bold;
color: #4A70F4;
}
.map-search-box {
display: flex;
gap: 10px;
align-items: center;
}
.map-search-input {
width: 300px;
}
#maplocation {
width: 100%;
height: calc(100vh - 200px);
min-height: 600px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
background: #f5f5f5;
}
/* 头像标记点样式 */
.tmap-marker-avatar {
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
object-fit: cover;
}
.info-window {
padding: 10px;
min-width: 150px;
}
.info-window-title {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.info-window-item {
font-size: 12px;
color: #666;
margin: 5px 0;
line-height: 1.6;
}
.info-window-item strong {
color: #333;
margin-right: 5px;
}
.map-controls {
position: absolute;
top: 20px;
right: 20px;
z-index: 1000;
background: #fff;
padding: 10px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.map-control-btn {
display: block;
width: 100%;
margin-bottom: 8px;
padding: 8px 15px;
text-align: center;
background: #4A70F4;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: all 0.3s;
}
.map-control-btn:hover {
background: #3a5fd4;
}
.map-control-btn:last-child {
margin-bottom: 0;
}
.map-legend {
position: absolute;
bottom: 20px;
left: 20px;
z-index: 1000;
background: rgba(255,255,255,0.95);
padding: 12px 15px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
font-size: 12px;
color: #666;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
margin: 5px 0;
}
.legend-color {
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
</style>
<div class="map-container">
<!-- 地图头部 -->
<div class="map-header">
<div class="map-header-left">
<div class="map-header-title">用户位置分布图</div>
<div class="map-header-stats">
<div class="stat-item">
<span class="stat-label">总用户数:</span>
<span class="stat-value" id="total-users">0</span>
</div>
<div class="stat-item">
<span class="stat-label">已定位:</span>
<span class="stat-value" id="located-users">0</span>
</div>
</div>
</div>
<div class="map-search-box">
<input type="text" id="search_key" class="layui-input map-search-input" placeholder="请输入地名搜索">
<!-- <button class="layui-btn layui-btn-normal searchKey">搜索</button>-->
<button class="layui-btn layui-btn-primary" id="reset-map">重置视图</button>
</div>
</div>
<!-- 地图容器 -->
<div style="position: relative;">
<div id="maplocation"></div>
<!-- 地图控制按钮 -->
<div class="map-controls">
<button class="map-control-btn" id="zoom-in">放大</button>
<button class="map-control-btn" id="zoom-out">缩小</button>
<button class="map-control-btn" id="fit-bounds">适应所有标记</button>
<button class="map-control-btn" id="clear-markers">清除标记</button>
</div>
<!-- 图例 -->
<div class="map-legend">
<div style="font-weight: bold; margin-bottom: 8px; color: #333;">图例</div>
<div class="legend-item">
<div class="legend-color" style="background: #4A70F4;"></div>
<span>用户位置</span>
</div>
</div>
</div>
</div>
<script src="/static/webjs/jquery.min.js"></script>
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service,tools&key=EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG" onload="console.log('腾讯地图API脚本加载完成')" onerror="console.error('腾讯地图API脚本加载失败')"></script>
<script>
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index'], function(){
var $ = layui.$;
// 用户数据(将从后端获取)
var users = [];
var markers = null;
var map = null;
var geocoder = null;
var infoWindows = [];
var defaultCenter = null; // 默认中心点(贵阳)
var defaultZoom = 12;
var markerClickHandler = null; // 保存点击事件处理函数
// 初始化地图
function initMap() {
try {
// 检查TMap是否已加载
if (typeof TMap === 'undefined') {
console.error('腾讯地图API未加载');
layer.msg('地图加载失败,请刷新页面重试', {icon: 2, time: 3000});
return;
}
// 初始化默认中心点
if (!defaultCenter) {
defaultCenter = new TMap.LatLng(26.647, 106.63); // 默认中心点(贵阳)
}
console.log('开始初始化地图...');
map = new TMap.Map('maplocation', {
zoom: defaultZoom,
center: defaultCenter
});
console.log('地图对象创建成功');
// 初始化标记点(样式将在渲染时动态创建)
markers = new TMap.MultiMarker({
map: map,
styles: {},
geometries: []
});
console.log('标记点对象创建成功');
// 初始化地理编码服务
geocoder = new TMap.service.Geocoder();
// 等待地图加载完成
map.on('complete', function() {
console.log('地图加载完成');
// 加载用户数据
setTimeout(function() {
if (typeof loadUsers === 'function') {
loadUsers();
} else {
console.error('loadUsers函数未定义');
}
}, 500);
});
// 备用方案如果complete事件没有触发延迟后直接初始化
setTimeout(function() {
console.log('备用方案延迟调用loadUsers()');
if (typeof loadUsers === 'function') {
loadUsers();
} else {
console.error('loadUsers函数未定义');
}
}, 3000);
// 监听地图错误
map.on('error', function(e) {
console.error('地图错误:', e);
layer.msg('地图加载出错,请刷新页面重试', {icon: 2, time: 3000});
});
} catch (error) {
console.error('地图初始化失败:', error);
layer.msg('地图初始化失败:' + error.message, {icon: 2, time: 3000});
}
}
// 加载用户数据
function loadUsers() {
if (typeof $ === 'undefined') {
console.error('jQuery未加载');
return;
}
$.ajax({
url: '{:url("BulletinBoard/getUserMap")}',
type: 'GET',
dataType: 'json',
success: function(res) {
console.log('用户数据响应:', res);
if (res.code === 1 && res.data) {
users = res.data;
console.log('获取到用户数据:', users.length, '条');
updateStats();
renderMarkers();
} else {
console.warn('获取用户数据失败:', res.msg);
layer.msg(res.msg || '获取用户数据失败', {icon: 2});
users = [];
updateStats();
}
},
error: function(xhr, status, error) {
console.error('请求失败:', xhr, status, error);
console.error('响应内容:', xhr.responseText);
layer.msg('请求失败,请稍后重试', {icon: 2});
users = [];
updateStats();
}
});
}
// 将loadUsers暴露到全局作用域方便调试
window.loadUsers = loadUsers;
// 更新统计信息
function updateStats() {
var totalUsers = users.length;
var locatedUsers = users.filter(function(u) {
return u.lat && u.lng && u.lat != 0 && u.lng != 0;
}).length;
$('#total-users').text(totalUsers);
$('#located-users').text(locatedUsers);
}
// 渲染标记点
function renderMarkers() {
if (!markers || !map) {
console.warn('标记点或地图未初始化');
return;
}
// 清除现有标记和信息窗口
clearMarkers();
var geometries = [];
var validUsers = [];
var markerStyles = {};
// 过滤有效坐标的用户
users.forEach(function(user, index) {
if (!user.lat || !user.lng || user.lat == 0 || user.lng == 0) {
return;
}
var lat = parseFloat(user.lat);
var lng = parseFloat(user.lng);
if (isNaN(lat) || isNaN(lng)) {
console.warn('无效的经纬度:', user);
return;
}
validUsers.push(user);
// 为每个用户创建使用头像的标记点样式
var styleId = 'avatar_' + (user.id || index);
// 如果没有头像,使用默认标记图标
var avatarUrl = user.avatar && user.avatar.trim() !== ''
? user.avatar
: 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png';
// 创建头像标记样式(圆形头像,带边框)
markerStyles[styleId] = new TMap.MarkerStyle({
width: 40,
height: 40,
anchor: {x: 20, y: 20},
src: avatarUrl
});
// 创建标记点
geometries.push({
id: 'user_' + (user.id || index),
position: new TMap.LatLng(lat, lng),
styleId: styleId,
properties: {
title: user.contact || user.name || '用户' + (user.id || index),
user: user
}
});
});
console.log('有效用户数:', validUsers.length, '标记点数:', geometries.length);
// 更新标记样式和几何图形
if (geometries.length > 0) {
// 先更新样式
markers.setStyles(markerStyles);
// 再更新几何图形
markers.updateGeometries(geometries);
// 创建信息窗口(但不自动显示)
createInfoWindows(validUsers);
// 如果只有一个用户,居中显示
if (validUsers.length === 1) {
map.setCenter(new TMap.LatLng(validUsers[0].lat, validUsers[0].lng));
map.setZoom(15);
} else if (validUsers.length > 1) {
// 适应所有标记
fitBounds(validUsers);
}
} else {
console.warn('没有有效的用户位置数据');
}
}
// 创建信息窗口(但不立即添加到地图,只在点击时创建)
function createInfoWindows(users) {
// 先清除之前的事件监听器(如果存在)
if (markers && markerClickHandler) {
try {
markers.off('click', markerClickHandler);
} catch (e) {
console.warn('移除事件监听器失败:', e);
}
markerClickHandler = null;
}
// 点击标记显示信息窗口
markerClickHandler = function(evt) {
var geometry = evt.geometry;
if (geometry && geometry.properties) {
// 关闭所有信息窗口
infoWindows.forEach(function(item) {
if (item.window) {
try {
item.window.close();
if (item.window.destroy) {
item.window.destroy();
}
} catch (e) {
console.warn('关闭信息窗口失败:', e);
}
}
});
infoWindows = [];
// 从 properties 中直接获取用户数据
var user = geometry.properties.user;
// 如果没有,尝试从全局 users 数组查找
if (!user) {
var userId = geometry.id.replace('user_', '');
user = users.find(function(u) {
return (u.id || '').toString() === userId || users.indexOf(u).toString() === userId;
});
}
if (user) {
var lat = parseFloat(user.lat);
var lng = parseFloat(user.lng);
if (isNaN(lat) || isNaN(lng)) {
console.error('无效的经纬度:', user);
return;
}
var content = '<div class="info-window">' +
'<div class="info-window-title">' + (user.contact || user.name || '用户') + '</div>' +
'<div class="info-window-item"><strong>电话:</strong>' + (user.telephone || user.phone || '-') + '</div>' +
'<div class="info-window-item"><strong>地址:</strong>' + (user.address || '-') + '</div>';
content += '</div>';
// 只在点击时创建信息窗口
try {
var infoWindow = new TMap.InfoWindow({
map: map,
position: new TMap.LatLng(lat, lng),
content: content,
visible: true,
zIndex: 100
});
infoWindows.push({
window: infoWindow,
markerId: geometry.id
});
} catch (e) {
console.error('创建信息窗口失败:', e);
layer.msg('显示信息失败', {icon: 2});
}
} else {
console.warn('未找到用户数据:', geometry.id);
}
} else {
console.warn('标记点数据不完整:', geometry);
}
};
// 绑定事件监听器
if (markers && markerClickHandler) {
markers.on('click', markerClickHandler);
}
}
// 适应所有标记
function fitBounds(users) {
if (users.length === 0) return;
var bounds = new TMap.LatLngBounds();
users.forEach(function(user) {
bounds.extend(new TMap.LatLng(parseFloat(user.lat), parseFloat(user.lng)));
});
map.fitBounds(bounds);
}
// 清除标记
function clearMarkers() {
if (markers) {
markers.updateGeometries([]);
}
infoWindows.forEach(function(item) {
if (item.window) {
try {
item.window.close();
if (item.window.destroy) {
item.window.destroy();
} else if (item.window.setMap) {
item.window.setMap(null);
}
} catch (e) {
console.warn('清除信息窗口失败:', e);
}
}
});
infoWindows = [];
}
// 搜索功能
var searchService = {
search: function(name) {
if (!name || !name.trim()) {
layer.msg('请输入搜索关键词', {icon: 0});
return;
}
geocoder.getLocation({ address: name })
.then(function(result) {
if (result && result.result && result.result.location) {
var location = result.result.location;
map.setCenter(location);
map.setZoom(15);
// 添加临时标记
markers.updateGeometries([{
id: 'search_result',
position: location,
styleId: 'marker'
}]);
} else {
layer.msg('未找到该地点', {icon: 0});
}
})
.catch(function(error) {
layer.msg('搜索失败:' + (error.message || '未知错误'), {icon: 2});
});
}
};
// 事件绑定
$(document).on('click', '.searchKey', function() {
var searchKey = $("#search_key").val();
searchService.search(searchKey);
});
$('#search_key').on('keypress', function(e) {
if (e.which === 13) {
$('.searchKey').trigger('click');
}
});
$('#reset-map').on('click', function() {
map.setCenter(defaultCenter);
map.setZoom(defaultZoom);
renderMarkers();
});
$('#zoom-in').on('click', function() {
map.setZoom(map.getZoom() + 1);
});
$('#zoom-out').on('click', function() {
map.setZoom(map.getZoom() - 1);
});
$('#fit-bounds').on('click', function() {
var validUsers = users.filter(function(u) {
return u.lat && u.lng && u.lat != 0 && u.lng != 0;
});
if (validUsers.length > 0) {
fitBounds(validUsers);
} else {
layer.msg('没有可显示的用户位置', {icon: 0});
}
});
$('#clear-markers').on('click', function() {
clearMarkers();
layer.msg('已清除所有标记', {icon: 1});
});
// 等待腾讯地图API加载完成后再初始化
var retryCount = 0;
var maxRetries = 100; // 10秒
function waitForTMap() {
if (typeof TMap !== 'undefined' && typeof TMap.Map !== 'undefined') {
// 延迟一下确保API完全加载
setTimeout(function() {
initMap();
}, 300);
} else {
// 如果还没加载等待一段时间后重试最多等待10秒
if (retryCount < maxRetries) {
retryCount++;
setTimeout(waitForTMap, 100);
} else {
console.error('腾讯地图API加载超时');
layer.msg('地图API加载超时请刷新页面重试', {icon: 2, time: 3000});
}
}
}
// 初始化
$(function() {
// 等待地图API加载
waitForTMap();
});
});
</script>

View File

@@ -22,6 +22,13 @@
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>商品类型:</label>
<div class="layui-input-block">
<input type="radio" name="type" value="1" title="服务商品" checked>
<input type="radio" name="type" value="2" title="普通商品">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">服务次数:</label>
<div class="layui-input-block">

View File

@@ -45,6 +45,13 @@
<input type="text" name="name" lay-verify="required" lay-verType="tips" placeholder="请输入名称" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>商品类型:</label>
<div class="layui-input-block">
<input type="radio" name="type" value="1" title="服务商品" checked>
<input type="radio" name="type" value="2" title="普通商品">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">父级分类:</label>
<div class="layui-input-inline">

View File

@@ -46,6 +46,13 @@
<input type="text" name="name" value="{$info.name}" lay-verify="required" lay-verType="tips" placeholder="请输入名称" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>商品类型:</label>
<div class="layui-input-block">
<input type="radio" name="type" value="1" title="服务商品" {if $info.type == 1} checked="checked" {/if}>
<input type="radio" name="type" value="2" title="普通商品" {if $info.type == 2} checked="checked" {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">父级分类:</label>
<div class="layui-input-inline">

View File

@@ -93,6 +93,14 @@
},
cols: [
{field: 'name', title: '分类名称',width: 280},
//类型 1服务商品 2普通商品
{field: 'type', title: '类型', width: 100, align: 'center',templet: function (d) {
if(d.type === 1){
return '服务商品';
}else{
return '普通商品';
}
}},
{field: '#image', title: '分类图标', width: 120,style:'height:100px;',toolbar: '#image', align: 'center'},
{templet: '#is_recommend', title: '首页推荐', width: 100,align: 'center'},
{templet: '#is_show', title: '显示', width: 100},

View File

@@ -0,0 +1,111 @@
{layout name="layout2" /}
<div class="">
<div class="layui-tab-content layui-form">
<div class="layui-tab-item layui-show">
<div class="layui-card-body" pad15>
<div lay-filter="">
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>发票信息ID</label>
<div class="layui-input-block">
<input type="number" name="invoice_id" lay-verify="required" lay-verType="tips"
autocomplete="off"
switch-tab="0" verify-msg="请输入发票信息ID" placeholder="请输入发票信息ID"
value="{$invoice.invoice_id}"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态:</label>
<div class="layui-input-inline">
<input type="radio" name="state" value="1" title="开票中" {if $invoice.state == 1}checked{/if}>
<input type="radio" name="state" value="2" title="驳回" {if $invoice.state == 2}checked{/if}>
<input type="radio" name="state" value="3" title="完成" {if $invoice.state == 3}checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发票文件:</label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn" id="upload-btn">上传PDF文件</button>
<div class="layui-upload-list" style="margin-top: 10px;">
<input type="hidden" name="document" id="document" value="{$invoice.document|default=''}">
<div id="file-list">
{if $invoice.document}
<div style="padding: 10px; background: #f5f5f5; border-radius: 4px; margin-bottom: 10px;">
<a href="{$invoice.document}" target="_blank">{$invoice.document}</a>
<span style="color: #5FB878;">已上传</span>
</div>
{/if}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item layui-hide">
<input type="button" lay-submit lay-filter="invoice-submit" id="invoice-submit" value="确认">
</div>
</div>
</div>
<script type="text/javascript">
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index', 'form', 'upload', 'jquery'], function () {
var $ = layui.$;
var form = layui.form;
var upload = layui.upload;
// 文件上传
upload.render({
elem: '#upload-btn',
url: '{:url("invoice/upload")}',
accept: 'file',
exts: 'pdf',
done: function(res){
if(res.code == 1){
$('#document').val(res.data.path);
$('#file-list').html('<div style="padding: 10px; background: #f5f5f5; border-radius: 4px;"><a href="' + res.data.path + '" target="_blank">' + res.data.path + '</a> <span style="color: #5FB878;">上传成功</span></div>');
layer.msg('上传成功', {icon: 1});
} else {
layer.msg(res.msg || '上传失败', {icon: 2});
}
},
error: function(){
layer.msg('上传失败', {icon: 2});
}
});
// 表单提交
form.on('submit(invoice-submit)', function(data){
var field = data.field;
var index = parent.layer.getFrameIndex(window.name);
$.ajax({
url: '{:url("invoice/edit")}?id={$invoice.id}',
type: 'post',
data: field,
success: function(res){
if(res.code == 1){
parent.layer.msg(res.msg, {icon: 1, time: 1000}, function(){
parent.layer.close(index);
parent.layui.table.reload('user-lists');
});
} else {
layer.msg(res.msg, {icon: 2});
}
},
error: function(){
layer.msg('请求失败', {icon: 2});
}
});
return false;
});
});
</script>

View File

@@ -0,0 +1,194 @@
{layout name="layout2" /}
<style>
.invoice-info-card {
margin-bottom: 15px;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.invoice-info-card .layui-card-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
font-weight: bold;
border-radius: 4px 4px 0 0;
}
.invoice-info-card .layui-card-header:first-child {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.info-row {
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
font-weight: 600;
color: #333;
min-width: 180px;
display: inline-block;
}
.info-value {
color: #666;
word-break: break-all;
}
.info-badge {
font-size: 13px;
padding: 4px 12px;
}
.file-preview {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
background: #f8f9fa;
border-radius: 4px;
border: 1px solid #e9ecef;
}
.section-divider {
height: 1px;
background: linear-gradient(to right, transparent, #e0e0e0, transparent);
margin: 20px 0;
}
</style>
<div class="">
<div class="layui-card-body" pad15>
<!-- 基本信息 -->
<div class="layui-card invoice-info-card">
<div class="layui-card-header">
<i class="layui-icon layui-icon-file"></i> 基本信息
</div>
<div class="layui-card-body">
<div class="info-row">
<span class="info-label">记录ID</span>
<span class="info-value">{$invoice.id}</span>
</div>
<div class="info-row">
<span class="info-label">发票信息ID</span>
<span class="info-value">{$invoice.invoice_id}</span>
</div>
<div class="info-row">
<span class="info-label">状态:</span>
<span class="info-value">
{if $invoice.state == 1}
<span class="layui-badge layui-bg-blue info-badge">待开票</span>
{elseif $invoice.state == 2}
<span class="layui-badge layui-bg-red info-badge">驳回</span>
{elseif $invoice.state == 3}
<span class="layui-badge layui-bg-green info-badge">完成</span>
{else}
<span class="layui-badge layui-bg-gray info-badge">未知</span>
{/if}
</span>
</div>
<div class="info-row">
<span class="info-label">发票文件:</span>
<span class="info-value">
{if $invoice.document}
<div class="file-preview">
<a href="{$invoice.document_url}" target="_blank" class="layui-btn layui-btn-primary layui-btn-xs">
<i class="layui-icon layui-icon-download-circle"></i> 查看文件
</a>
<span style="color: #999; font-size: 12px;">{$invoice.document_url}</span>
</div>
{else}
<span style="color: #999;">无文件</span>
{/if}
</span>
</div>
<div class="info-row">
<span class="info-label">创建时间:</span>
<span class="info-value">{$invoice.create_time|default='--'}</span>
</div>
<div class="info-row">
<span class="info-label">更新时间:</span>
<span class="info-value">{$invoice.update_time|default='--'}</span>
</div>
</div>
</div>
<!-- 订单信息 -->
<div class="layui-card invoice-info-card">
<div class="layui-card-header">
<i class="layui-icon layui-icon-cart"></i> 订单信息
</div>
<div class="layui-card-body">
<div class="info-row">
<span class="info-label">订单编号:</span>
<span class="info-value">{$invoice.order.order_sn ?? "--"}</span>
</div>
<div class="info-row">
<span class="info-label">订单金额:</span>
<span class="info-value" style="color: #f56c6c; font-weight: bold; font-size: 16px;">
¥{$invoice.order.settle_amount ?? "0.00"}
</span>
</div>
</div>
</div>
<!-- 用户信息 -->
<div class="layui-card invoice-info-card">
<div class="layui-card-header">
<i class="layui-icon layui-icon-user"></i> 用户信息
</div>
<div class="layui-card-body">
<div class="info-row">
<span class="info-label">系统用户名:</span>
<span class="info-value">{$invoice.invoice.user.nickname ?? "--"}</span>
</div>
</div>
</div>
<!-- 发票抬头信息 -->
<div class="layui-card invoice-info-card">
<div class="layui-card-header">
<i class="layui-icon layui-icon-form"></i> 发票抬头信息
</div>
<div class="layui-card-body">
<div class="info-row">
<span class="info-label">类型:</span>
<span class="info-value">
{if $invoice.invoice.type == 1}
<span class="layui-badge layui-bg-blue info-badge">个人</span>
{elseif $invoice.invoice.type == 2}
<span class="layui-badge layui-bg-orange info-badge">企业</span>
{else}
<span class="layui-badge layui-bg-gray info-badge">未知</span>
{/if}
</span>
</div>
<div class="info-row">
<span class="info-label">发票类型:</span>
<span class="info-value">{$invoice.invoice.invoice_type ?? "--"}</span>
</div>
<div class="info-row">
<span class="info-label">发票抬头/企业名称:</span>
<span class="info-value" style="font-weight: 500;">{$invoice.invoice.invoice_title ?? "--"}</span>
</div>
<div class="info-row">
<span class="info-label">纳税人识别号/统一社会信用代码:</span>
<span class="info-value" style="font-family: 'Courier New', monospace; letter-spacing: 1px;">
{$invoice.invoice.identification ?? "--"}
</span>
</div>
<div class="info-row">
<span class="info-label">注册地址:</span>
<span class="info-value">{$invoice.invoice.address ?? "--"}</span>
</div>
<div class="info-row">
<span class="info-label">注册电话:</span>
<span class="info-value">{$invoice.invoice.phone ?? "--"}</span>
</div>
<div class="info-row">
<span class="info-label">开户行:</span>
<span class="info-value">{$invoice.invoice.bank ?? "--"}</span>
</div>
<div class="info-row">
<span class="info-label">开户账号:</span>
<span class="info-value" style="font-family: 'Courier New', monospace;">
{$invoice.invoice.account ?? "--"}
</span>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,189 @@
{layout name="layout1" /}
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-collapse like-layui-collapse" lay-accordion="" style="border:1px dashed #c4c4c4">
<div class="layui-colla-item">
<h2 class="layui-colla-title like-layui-colla-title" style="background-color: #fff">操作提示</h2>
<div class="layui-colla-content layui-show">
<p>*发票管理列表,对发票进行编辑、查看和删除等操作。</p>
</div>
</div>
</div>
</div>
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 200px;">
<input type="text" id="keyword" name="keyword" placeholder="请输入订单号或发票ID" autocomplete="off" class="layui-input">
</div>
<div class="layui-input-inline" style="width: 150px;">
<select name="state" id="state">
<option value="">全部状态</option>
<option value="1">待开票</option>
<option value="2">驳回</option>
<option value="3">完成</option>
</select>
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<button class="layui-btn layui-btn-sm layuiadmin-btn-user {$view_theme_color}" lay-submit lay-filter="user-search">查询</button>
<button class="layui-btn layui-btn-sm layuiadmin-btn-user layui-btn-primary " lay-submit lay-filter="user-clear-search">清空查询</button>
<!-- <button class="layui-btn layui-btn-sm layuiadmin-btn-user {$view_theme_color}" data-type="add">添加</button>-->
</div>
</div>
</div>
<div class="layui-card-body">
<table id="user-lists" lay-filter="user-lists"></table>
<script type="text/html" id="user-operation">
<a class="layui-btn layui-btn-primary layui-btn-sm" lay-event="info">资料</a>
<a class="layui-btn layui-btn-normal layui-btn-sm" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-sm" lay-event="del">删除</a>
</script>
<script type="text/html" id="status">
{{# if(d.state == 1){ }}
<span class="layui-badge layui-bg-blue">待开票</span>
{{# } else if(d.state == 2){ }}
<span class="layui-badge layui-bg-red">驳回</span>
{{# } else if(d.state == 3){ }}
<span class="layui-badge layui-bg-green">完成</span>
{{# } else { }}
<span class="layui-badge layui-bg-gray">未知</span>
{{# } }}
</script>
<script type="text/html" id="document-templet">
{{# if(d.document){ }}
<a href="{{ d.document }}" target="_blank" class="layui-btn layui-btn-xs layui-btn-primary">查看文件</a>
{{# } else { }}
<span>无文件</span>
{{# } }}
</script>
</div>
</div>
</div>
<style>
.layui-table-cell {
height: auto;
}
</style>
<script>
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index','table','like'], function(){
var $ = layui.$
,form = layui.form
,table = layui.table
,like = layui.like;
//监听搜索
form.on('submit(user-search)', function(data){
var field = data.field;
table.reload('user-lists', {
where: field,
page: {
curr: 1
}
});
});
//清空查询
form.on('submit(user-clear-search)', function(data){
$('#keyword').val('');
$('#state').val('');
form.render('select');
table.reload('user-lists', {
where: {},
page: {
curr: 1
}
});
});
//监听工具条
table.on('tool(user-lists)', function(obj){
var data = obj.data;
if(obj.event === 'info'){
layer.open({
type: 2
,title: '发票详情'
,content: '{:url("invoice/info")}?id=' + data.id
,area: ['800px', '600px']
,btn: ['关闭']
,yes: function(index){
layer.close(index);
}
});
} else if(obj.event === 'edit'){
layer.open({
type: 2
,title: '编辑发票'
,content: '{:url("invoice/edit")}?id=' + data.id
,area: ['800px', '600px']
,btn: ['确定', '取消']
,yes: function(index, layero){
var iframeWindow = window['layui-layer-iframe'+ index]
,submit = layero.find('iframe').contents().find("#invoice-submit");
submit.trigger('click');
}
});
} else if(obj.event === 'del'){
layer.confirm('确定删除吗?', function(index){
$.ajax({
url: '{:url("invoice/del")}',
type: 'post',
data: {id: data.id},
success: function(res){
if(res.code == 1){
layer.msg(res.msg, {icon: 1, time: 1000}, function(){
layer.close(index);
table.reload('user-lists');
});
} else {
layer.msg(res.msg, {icon: 2});
}
}
});
});
}
});
$('.layui-btn.layuiadmin-btn-user').on('click', function(){
var type = $(this).data('type');
active[type] ? active[type].call(this) : '';
});
//管理员管理
table.render({
id:'user-lists'
,elem: '#user-lists'
,url: '{:url("invoice/lists")}'
,cols: [[
{type: 'checkbox'}
,{field: 'id', title: 'ID', width: 80}
,{field: 'invoice_id', title: '发票信息ID'}
,{field: 'state', title: '状态', align: 'center', templet: '#status'}
,{field: 'document', title: '发票文件', align: 'center', templet: '#document-templet'}
,{field: 'create_time', title: '创建时间'}
,{field: 'update_time', title: '更新时间'}
,{fixed: 'right', title: '操作', width: 280, align: 'center', toolbar: '#user-operation'}
]]
,page: true
,text: {none: '暂无数据!'}
,parseData: function(res){
return {
"code": res.code,
"msg": res.msg,
"count": res.data.count,
"data": res.data.lists,
};
}
});
});
</script>

View File

@@ -0,0 +1,982 @@
{layout name="layout2" /}
<link rel="stylesheet" href="/static/admin/css/goods.css" media="all">
<div class="">
<div class="layui-tab-content layui-form">
<div class="layui-tab-item goods-content layui-show">
<div class="layui-card-body" pad15>
<div lay-filter="">
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>标题:</label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="custom_required" lay-verType="tips"
autocomplete="off"
switch-tab="0" verify-msg="请输入标题" placeholder="请输入标题"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>员工:</label>
<div class="layui-input-block">
<select name="sid" lay-verType="" placeholder="请选择员工" switch-tab="0" verify-msg="请选择员工">
<option value="">请选择员工</option>
{foreach $staff as $key=>$vo}
<option value="{$vo.id}">{$vo.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>产品:</label>
<div class="layui-input-block">
<select name="goods_id" lay-verType="" placeholder="请选择关联产品" switch-tab="0" verify-msg="请选择关联产品">
<option value="">请选择关联产品</option>
{foreach $goods as $key=>$vo}
<option value="{$vo.id}">{$vo.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">显示状态:</label>
<div class="layui-input-inline">
<input type="radio" name="state" value="1" title="显示" checked>
<input type="radio" name="state" value="2" title="不显示">
</div>
</div>
<div class="layui-form-item" style="margin-bottom: 0px">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>图片:</label>
<div style="min-height:80px;">
<ul style="list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 10px;">
<!-- 图片列表将在这里显示 -->
</ul>
<div class="goods-img-add" lay-verify="goods_image" lay-verType="tips" switch-tab="0" verify-msg="选择图片" style="margin-top: 10px;"></div>
</div>
<br>
</div>
<div class="layui-form-item">
<label class="layui-form-label">内容详情</label>
<div class="layui-input-block">
<textarea name="content" id="content" lay-verify="content" class="field-content"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item layui-hide">
<input type="button" lay-submit lay-filter="add-user_group-submit" id="add-user_group-submit" value="确认">
</div>
</div>
</div>
<!---------------------------------html模板---------------------------------->
<script type="text/html" id="template-goods-image">
<li class="goods-li">
<input name="goods_image[]" type="hidden" value="{image-src}">
<img class="goods-img goods_image" src="{image-src}">
<a class="goods-img-del-x" style="display: none;">x</a></li>
</script>
<script type="text/javascript">
//注意:选项卡 依赖 element 模块,否则无法进行功能性操作
var dragstart = 0;
var swop_element_ed = -1;
var create_table_by_spec = null;
var spec_table_data = [];
var spec_value_temp_id_number = 0;
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['index', 'element', 'jquery', 'like', 'likeedit'], function () {
var $ = layui.$;
var like = layui.like;
var form = layui.form;
var likeedit = layui.likeedit;
$('.first_ratio').bind('input propertychange', function() {
var that = $(this);
var value = that.val();
format(that,value)
});
$('.second_ratio').bind('input propertychange', function() {
var that = $(this);
var value = that.val();
format(that,value)
});
$('.three_ratio').bind('input propertychange', function() {
var that = $(this);
var value = that.val();
format(that,value)
});
//格式化分销比例
function format(that,value){
value = value.replace(/[^0-9.]/g,'');
value = value.replace(/\.{2,}/g,".");
value = value.replace(".","$#$").replace(/\./g,"").replace("$#$",".");
value = value.replace(/^(\-)*(\d+)\.(\d)(\d).*$/,'$1$2.$3$4');
if(value.indexOf(".")< 0 && value !=""){
value= parseFloat(value);
}
if(value > 100){
that.val(100);
return false;
}
that.val(value);
}
//------------------------基本信息----------------------------------
//商品主图事件
$(document).on('mouseenter', '.goods-li', function () {
$(this).children().last().show();
});
$(document).on('mouseleave', '.goods-li', function () {
$(this).children().last().hide();
});
$(document).on('click', '.goods-li', function () {
var src = $(this).children('img').attr('src');
like.showImg(src,600);
});
//主图显示删除按钮
$(document).on('mouseenter','.master-image',function () {
$(this).children('.goods-image-del').show();
})
$(document).on('mouseleave', '.master-image', function () {
$(this).children('.goods-image-del').hide();
});
$(document).on('click', '.master-image', function () {
var src = $(this).children('img').attr('src');
like.showImg(src,600);
});
//分享海报删除按钮
$(document).on('mouseenter','.poster-image',function () {
$(this).children('.goods-image-del').show();
})
$(document).on('mouseleave', '.poster-image', function () {
$(this).children('.goods-image-del').hide();
});
$(document).on('click', '.poster-image', function () {
var src = $(this).children('img').attr('src');
like.showImg(src,600);
});
$(document).on('click', '.goods-img-del-x', function () {
if($(this).hasClass('goods-image-del')){
$(this).parent().next().show();
$(this).parent().children().remove();
}
$(this).parent().remove();
return false;
});
like.imageUpload('.goods-img-add', function (uris, element) {
// 海报上传逻辑(单图片)
if(element.hasClass('poster-upload')){
if(uris.length>1){
layer.msg('最多只能选中1张图片');
return;
}
var html = '' +
'<input name="poster" type="hidden" value="' + like.getUrlFileName(uris[0], '{$storageUrl}') + '">' +
' <img class="goods-img" src="' + uris[0] + '">\n' +
'<a class="goods-img-del-x goods-image-del">x</a>';
element.prev().append(html);
element.css('display','none');
return 0;
}
// 多图片上传逻辑最多8张
var count = element.prev().children().length;
count = !count ? 0 : count;
if (count + uris.length > 8) {
layer.msg('最多只能选中8张图片');
return;
}
uris = uris.reverse();
for(var i in uris){
var uri = uris[i];
if (!uri) continue; // 跳过无效的 uri
var template_goods_image = $('#template-goods-image').html();
if (!template_goods_image) {
console.error('模板 template-goods-image 不存在');
continue;
}
// 获取文件路径(用于 hidden input
var filePath = like.getUrlFileName ? like.getUrlFileName(uri, '{$storageUrl}') : uri;
if (!filePath) {
filePath = uri; // 如果 getUrlFileName 返回 undefined使用原始 uri
}
// 先替换 input 中的 {image-src},再替换 img 中的 {image-src}
var html = template_goods_image.replace('{image-src}', filePath);
html = html.replace('{image-src}', uri);
element.prev().append(html);
}
}, true);
// ========= 视频上传 开始
$('#video').click(function(){
layer.open({
type: 2,
title: '上传视频',
content: '{:url("file_new/lists")}?type=video',
area: ['90%', '90%']
});
});
window.videoCallback = function(uris) {
like.videoCallback(uris);
};
// ========== 视频上传 结束
//图片拉拽
$(document).on('dragstart', '.goods-li', function (e) {
dragstart = e.offsetX;
});
$(document).on('dragend', '.goods-li', function (e) {
swop_element_ed = -1;
});
$(document).on('mousedown', '.goods-li', function (e) {
swop_element_ed = -1;
});
$(document).on('dragover', '.goods-li', function (e) {
e.preventDefault();
swop_element = parseInt((e.clientX - dragstart) / 100) - 1;
swop_element = swop_element <= 0 ? 0 : swop_element;
my_element = $(this).prevAll().length;
if (swop_element == my_element) {
return;
}
if (swop_element == swop_element_ed) {
return;
}
swop_element_ed = swop_element;
swop($(this), $(this).parent().children().eq(swop_element));
});
//渲染品牌
var brands = 1;
setBrand();
function setBrand(default_id) {
var brand_select_html = '<option value="">请选择原料分类</option>';
for (var i in brands) {
brand_select_html += '<option value="' + brands[i]['id'] + '">' + brands[i]['name'] + '</option>';
}
$('select[name="brand_id"]').html(brand_select_html);
$('select[name="brand_id"]').val(default_id);
form.render('select');
}
//渲染隶属部门
var bellist = 1;
console.log(bellist);
setBrands();
function setBrands(default_id) {
var bellist_select_html = '<option value="">请选择部门</option>';
for (var i in bellist) {
bellist_select_html += '<option value="' + bellist[i]['id'] + '">' + bellist[i]['name'] + '</option>';
}
$('select[name="bellist_id"]').html(bellist_select_html);
$('select[name="bellist_id"]').val(default_id);
form.render('select');
}
//------------------------价格库存----------------------------------
//统一规格与多规格切换事件
function switchSpecType(value)
{
var goods_spec_project = $('#goods-spec-project');
if (value == 2) {
$('#add-spec').parent().show();
if (goods_spec_project.children().length > 0) {
goods_spec_project.parent().show();
$('#more-spec-lists').show();
}
$('#one-spec-lists').hide();
} else {
$('#add-spec').parent().hide();
goods_spec_project.parent().hide();
$('#one-spec-lists').show();
$('#more-spec-lists').hide();
}
}
form.on('radio(spec-type)', function (data) {
switchSpecType(data.value);
});
//添加规格项目
function addSpec(value){
value = value===undefined?'':value;
var element_spec = $('#goods-spec-project');
var count = $('.goods-spec').size();
if (count > 2) {
layer.msg('最多添加3个规格项目');
return;
}
var template_spec = $('#template-spec').html();
element_spec.append(template_spec.replace('{value}',value));
$('#goods-spec-project').parent().show();
form.render('checkbox');
}
$('#add-spec').click(function () {
addSpec();
});
//显示或隐藏规格项目删除按钮绑定
$(document).on('mouseenter', '.goods-spec', function () {
$(this).find('.goods-spec-del-x').show();
});
$(document).on('mouseleave', '.goods-spec', function () {
$(this).find('.goods-spec-del-x').hide();
});
//删除规格项目绑定
$(document).on('click', '.goods-spec-del-x', function () {
$(this).parent().remove();
var goods_spec_project = $('#goods-spec-project');
if (goods_spec_project.children().length == 0) {
goods_spec_project.parent().hide();
}
});
//添加或删除规格项目后续操作
function specValueLater(){
$('.add-spec-value').each(function(){
add_spec_value = $(this);
var spec_values = '';
add_spec_value.parent().parent().find('.goods-spec-value-input').each(function () {
spec_values += $(this).val() + ',';
});
add_spec_value.parent().find('.spec_values').val(spec_values.substring(0, spec_values.lastIndexOf(',')));
var spec_value_ids = '';
add_spec_value.parent().parent().find('.goods-sepc-value-id-input').each(function () {
spec_value_ids += $(this).val() + ',';
});
add_spec_value.parent().find('.spec_value_ids').val(spec_value_ids.substring(0, spec_value_ids.lastIndexOf(',')));
triggerCreateTableBySepc();
});
}
//添加规格项
function addSpecvalue(add_spec_value,spec,spec_id){
var template_spec_value = $('#template-spec-value').html();
var template_spec_value_html = template_spec_value.replace('{spec_value_temp_id}', spec_value_temp_id_number--);
template_spec_value_html = template_spec_value_html.replace('{spec_value_id}',spec_id);
template_spec_value_html = template_spec_value_html.replace('{spec_value}', spec)
add_spec_value.parent().before(template_spec_value_html);
var div = add_spec_value.parent().parent().parent().parent();
var status = div.find('.batch-spec-image-switch').first().is(':checked');
if(status){
div.find('.batch-spec-image').show();
}else{
div.find('.batch-spec-image').hide();
}
}
form.on('switch(batch-spec-image-switch)', function(data){
var status = data.elem.checked;
$('.batch-spec-image-switch').prop("checked",false);
if(status) {
$('.batch-spec-image').hide();
$(this).parent().parent().parent().parent().find('.batch-spec-image').show();
$(this).prop("checked",true);
}else{
$(this).parent().parent().parent().parent().find('.batch-spec-image').hide();
}
form.render('checkbox');
});
//批量添加规格项绑定
$(document).on('click', '.add-spec-value', function () {
var add_spec_value = $(this);
layer.prompt({title: '输入规格值,多个请换行', formType: 2}, function (text, index) {
layer.close(index);
var specs = text.split('\n');
for (var i in specs) {
specs[i] = specs[i].trim();
}
specs = unique(specs);
var added_specs = [];
add_spec_value.parent().parent().find('.goods-spec-value-input').each(function () {
added_specs.push($(this).val().trim());
});
for (var i in specs) {
var spec = specs[i].trim();
if (spec == '' || in_array(spec, added_specs)) {
//已存或为空的不添加
continue;
}
addSpecvalue(add_spec_value,spec,0);
}
specValueLater();
});
});
//删除规格项
$(document).on('click', '.goods-spec-value-del-x', function () {
var add_spec_value = $(this).parent().parent().find('.add-spec-value').first();
$(this).parent().remove();
specValueLater();
});
//批量填充规格图片
like.imageUpload('.batch-spec-image',function(uri,element){
var temp_id = element.prev().attr('spec-value-temp-id');
var spec_images = null;
if($('input[name="spec_type"]:checked').val() == 1){
spec_images = $('#one-spec-lists-table input[name="spec_image[]"]');
}else{
spec_images = $('#more-spec-lists-table input[name="spec_image[]"]');
}
spec_images.each(function(){
var temp_ids = $(this).parent().parent().parent().attr('spec-value-temp-ids');
temp_ids_arr = temp_ids.split(',');
var key = $(this).attr('name') + temp_ids;
if(in_array(temp_id,temp_ids_arr)) {
spec_table_data[key] = uri;
}
});
setTableValue();
});
//批量填充
$('.batch-spec-content').click(function(){
var title = $(this).text();
var input_name = $(this).attr('input-name');
layer.prompt({
formType: 3
,title: '批量填写'+title
},function(value, index, elem){
$('input[name="'+input_name+'[]"]').val(value);
//保存值到本地
$('#more-spec-lists-table input').each(function(){
var key = $(this).attr('name') + $(this).parent().parent().attr('spec-value-temp-ids');
spec_table_data[key] = $(this).val();
});
layer.close(index);
});
});
//显示或隐藏规格项删除按钮
$(document).on('mouseenter', '.goods-spec-value', function () {
$(this).find('.goods-spec-value-del-x').show();
});
$(document).on('mouseleave', '.goods-spec-value', function () {
$(this).find('.goods-spec-value-del-x').hide();
});
// 单规格图片
like.imageUpload('.goods-one-spec-img-add', function (uris, element) {
if(uris.length>1){
layer.msg('最多最能选中1张图片');
return;
}
element.hide();
var key = element.parent().parent().parent().attr('spec-value-temp-ids');
spec_table_data["spec_image[]"+key] = uris[0];//保存图片地址
$(element).parent().html('<input name="one_spec_image" type="hidden" value="' + like.getUrlFileName(uris[0], '{$storageUrl}') + '"><a class="goods-one-spec-img-del-x">x</a><img class="goods-spec-img" src="' + uris[0] + '">');
});
$(document).on('mouseenter', '.goods-spec-img-div', function () {
$(this).find('.goods-one-spec-img-del-x').show();
});
$(document).on('mouseleave', '.goods-spec-img-div', function () {
$(this).find('.goods-one-spec-img-del-x').hide();
});
$(document).on('click', '.goods-one-spec-img-del-x', function () {
$(this).parent().html('<input type="hidden" name="one_spec_image"><img src="/static/common/image/plug/goods-lists-add-image.png" class="goods-one-spec-img-add">');
});
//多规格图片
like.imageUpload('.goods-spec-img-add', function (uris, element) {
if(uris.length>1){
layer.msg('最多最能选中1张图片');
return;
}
element.hide();
var key = element.parent().parent().parent().attr('spec-value-temp-ids');
spec_table_data["spec_image[]"+key] = uris[0];//保存图片地址
$(element).parent().html('<input name="spec_image[]" type="hidden" value="' + like.getUrlFileName(uris[0], '{$storageUrl}') + '"><a class="goods-spec-img-del-x">x</a><img class="goods-spec-img" src="' + uris[0] + '">');
});
$(document).on('mouseenter', '.goods-spec-img-div', function () {
$(this).find('.goods-spec-img-del-x').show();
});
$(document).on('mouseleave', '.goods-spec-img-div', function () {
$(this).find('.goods-spec-img-del-x').hide();
});
$(document).on('click', '.goods-spec-img-del-x', function () {
var key = 'spec_image[]' + $(this).parent().parent().parent().attr('spec-value-temp-ids');
$(this).parent().html('<input type="hidden" name="spec_image[]"><img src="/static/common/image/plug/goods-lists-add-image.png" class="goods-spec-img-add">');
spec_table_data[key] = '';
});
$(document).on('click', '.goods-spec-img', function () {
like.showImg($(this).attr('src'),600);
});
//规格生成表格
createTableBySepc = function () {
if ($('.goods-spec').size() <= 0) {
$('#more-spec-lists').hide();
return;
}
$('#more-spec-lists').show();
var table_title = [];
var table_data = [];
var spec_value_temp_arr = [];
var i = 0;
var th_html = $('#template-spec-table-th').html();
var tr_html = $('#template-spec-table-tr').html();
//遍历规格项目
$('.goods-spec').each(function () {
var spec_name = $(this).find('.spec_name').first().val();
if (isEmptyString(spec_name)) {
return true;
}
table_title[i] = spec_name;
table_data[i] = [];
spec_value_temp_arr[i] = [];
var j = 0;
$(this).find('.goods-spec-value .goods-spec-value-input').each(function () {
var spec_value = $(this).val();
var spec_value_temp_id = $(this).attr('spec-value-temp-id');
if (isEmptyString(spec_value)) {
return true;
}
table_data[i][j] = spec_value;
spec_value_temp_arr[i][j] = spec_value_temp_id;
j++;
});
i++;
});
table_html = '';
//表格头部组装
spec_th_html = '';
for (var i in table_title) {
spec_th_html += '<th>' + table_title[i] + '</th>';
}
table_html = th_html.replace('{spec_th}', spec_th_html);
spec_value_temp_arr = cartesianProduct(spec_value_temp_arr);
table_data = cartesianProduct(table_data);
for (var i in table_data) {
var spec_tr_html = '';
var tr_name_arr = [];
var specs = '';
if (Array.isArray(table_data[i])) {
//根据规格创建tr的id
var spec_value_temp_ids = '';
for(var j in spec_value_temp_arr[i]){
spec_value_temp_ids += spec_value_temp_arr[i][j]+',';
}
spec_value_temp_ids = spec_value_temp_ids.substring(0, spec_value_temp_ids.lastIndexOf(','));
spec_tr_html += '<tr spec-value-temp-ids="'+spec_value_temp_ids+'">';
for (var j in table_data[i]) {
spec_tr_html += '<td>' + table_data[i][j] + '</td>';
tr_name_arr[j] = table_data[i][j];
specs += table_data[i][j].replace(',', '') + ',';
}
} else {
var spec_value_temp_ids = spec_value_temp_arr[i];
spec_tr_html = '<tr spec-value-temp-ids="'+spec_value_temp_ids+'">';
spec_tr_html += '<td>' + table_data[i] + '</td>';
specs += table_data[i].replace(',', '') + ',';
}
specs = specs.substring(0, specs.lastIndexOf(','));
spec_table_data["spec_value_str[]"+spec_value_temp_ids] = specs;
spec_tr_html += '<td style="display: none"><input type="hidden" name="spec_value_str[]" value="' + specs + '"><input type="hidden" name="item_id[]" value=""></td>';
table_html += tr_html.replace('{spec_td}', spec_tr_html);
}
$('#more-spec-lists-table').html(table_html);
setTableValue();
}
//触发规格生成表格
function triggerCreateTableBySepc() {
clearTimeout(create_table_by_spec);
create_table_by_spec = setTimeout(createTableBySepc, 1000);
}
//各种触发生成规格事件
triggerCreateTableBySepc();
$('#add-spec').click(function () {
triggerCreateTableBySepc();
});
$(document).on('click', '.goods-spec-del-x', function () {
triggerCreateTableBySepc();
});
$(document).on('click', '.add-spec-value', function () {
triggerCreateTableBySepc();
});
$(document).on('click', '.goods-spec-value-del-x', function () {
triggerCreateTableBySepc();
});
$(document).on('input', '.goods-spec input', function () {
triggerCreateTableBySepc();
specValueLater();
});
//规格数据本地保存
$(document).on('input', '#more-spec-lists-table input', function () {
var key = $(this).attr('name') + $(this).parent().parent().attr('spec-value-temp-ids');
spec_table_data[key] = $(this).val();
});
//动态渲染已保存的值
function setTableValue() {
$('#more-spec-lists-table').find('input').each(function () {
var key = $(this).attr('name') + $(this).parent().parent().attr('spec-value-temp-ids');
if(spec_table_data[key]!== undefined){
$(this).val(spec_table_data[key]);
}
});
$('.goods-spec-img-div').each(function(){
var key = $(this).parent().parent().attr('spec-value-temp-ids');
if(spec_table_data["spec_image[]"+key]){
$(this).html('<input name="spec_image[]" type="hidden" value="' + spec_table_data["spec_image[]"+key] + '"><a class="goods-spec-img-del-x">x</a><img class="goods-spec-img" src="' + spec_table_data["spec_image[]"+key] + '">');
}
});
}
likeedit.set({
uploadImage: {
url: "{:url('file/image')}",
type: 'post'
}
});
var likeedit_index = likeedit.build('content'); //建立编辑器
form.verify({
content: function () {
likeedit.sync(likeedit_index)
}
});
//------------------------数据验证----------------------------------
function switchTab(number) {
$('.goods-tab').removeClass('layui-this');
$('.goods-content').removeClass('layui-show');
$('.goods-tab').eq(number).addClass('layui-this');
$('.goods-content').eq(number).addClass('layui-show');
}
form.verify({
custom_required: function (value, item) {
if (!value) {
switchTab($(item).attr('switch-tab'));
return $(item).attr('verify-msg');
}
}
,image:function(value, item) {
var image = $('input[name="image"]').val();
if(!image){
switchTab($(item).attr('switch-tab'));
return $(item).attr('verify-msg');
}
}
,goods_image: function (value, item) {
// 检查是否有上传的图片
var imageCount = $('input[name="goods_image[]"]').filter(function() {
return $(this).val() != '';
}).length;
if (imageCount == 0) {
switchTab($(item).attr('switch-tab'));
return $(item).attr('verify-msg');
}
},
status:function(value,item){
if(!$('input[name="status"]:checked').val()){
return $(item).attr('verify-msg');
}
},
one_spec_required: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 1) {
if (!value) {
switchTab($(item).attr('switch-tab'));
return $(item).attr('verify-msg');
}
}
},
add_more_spec: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if ($('#more-spec-lists-table tbody tr').length == 0) {
switchTab($(item).attr('switch-tab'));
return $(item).attr('verify-msg');
}
}
},
delivery: function (value, item) {
var choose = 0;
$('.delivery').each(function () {
if ($(this).is(':checked')) {
choose++;
}
});
if (choose == 0) {
switchTab($(item).attr('switch-tab'));
return $(item).attr('verify-msg');
}
},
one_volume: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 1) {
if (value && value < 0) {
switchTab($(item).attr('switch-tab'));
return '体积必须大于0';
}
}
},
one_weight: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 1) {
if (value && value < 0) {
switchTab($(item).attr('switch-tab'));
return '重量必须大于0';
}
}
},
one_price: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0.01) {
switchTab($(item).attr('switch-tab'));
return '价格必须大于或等于0.01';
}
}
},
one_cost_price: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0.01) {
switchTab($(item).attr('switch-tab'));
return '成本价格必须大于或等于0.01';
}
}
},
more_price:function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0.01) {
switchTab($(item).attr('switch-tab'));
return '价格必须大于或等于0.01';
}
}
},
more_cost_price:function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0.01) {
switchTab($(item).attr('switch-tab'));
return '成本价格必须大于或等于0.01';
}
}
},
more_stock: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0) {
switchTab($(item).attr('switch-tab'));
return '库存必须大于0';
}
}
},
more_weight: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0) {
switchTab($(item).attr('switch-tab'));
return '重量必须大于0';
}
}
},
more_volume: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
if (value && value < 0) {
switchTab($(item).attr('switch-tab'));
return '体积必须大于0';
}
}
},
repetition_spec_name: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
var spec_names = [];
$('.spec_name').each(function () {
spec_names.push($(this).val());
});
if ((new Set(spec_names)).size != spec_names.length) {
switchTab($(item).attr('switch-tab'));
return '规格名称重复';
}
}
},
repetition_spec_value: function (value, item) {
if ($('input[name="spec_type"]:checked').val() == 2) {
var spec_values = [];
$(item).find('.goods-spec-value-input').each(function () {
spec_values.push($(this).val());
});
if ((new Set(spec_values)).size != spec_values.length) {
switchTab($(item).attr('switch-tab'));
return '同一规格中,规格值不能重复';
}
}
},
distribution:function (value,item) {
var first_ratio = parseFloat($('.first_ratio').val());
var second_ratio = parseFloat($('.second_ratio').val());
var three_ratio = parseFloat(value);
if(first_ratio + second_ratio + three_ratio > 100){
return '分销比例不可超过100';
}
}
});
//------------------------数据验证----------------------------------
//------------------------编辑页面----------------------------------
{notempty name='info'}
var goods_info= {$info|raw|default=''};
// console.log(goods_info);
$('input[name="goods_id"]').val(goods_info['base']['id']);
$('input[name="name"]').val(goods_info['base']['name']);
$('input[name="mobile"]').val(goods_info['base']['mobile']);
$('input[name="age"]').val(goods_info['base']['age']);
setBrand(goods_info['base']['group_id']); //渲染品牌
setBrands(goods_info['base']['bellist_id']); //渲染品牌
$('input[name="remark"]').val(goods_info['base']['remark']);
// 渲染多图片
var image_html = '';
for(var i in goods_info['base']['goods_image']) {
var template_goods_image = $('#template-goods-image').html();
if (!template_goods_image) {
console.error('模板 template-goods-image 不存在');
continue;
}
// 先替换 input 中的 {image-src},再替换 img 中的 {image-src}
var html = template_goods_image.replace('{image-src}', goods_info['base']['goods_image'][i]['uri'] || '');
html = html.replace('{image-src}', goods_info['base']['goods_image'][i]['abs_image'] || '');
image_html += html;
}
if(image_html) {
$('.goods-img-add').prev('ul').append(image_html);
}
//渲染商品主图(如果还有主图字段的话)
if(goods_info['base']['image']){
var html = '' +
'<input name="image" type="hidden" value="' + goods_info['base']['image'] + '">' +
' <img class="goods-img" src="' + goods_info['base']['abs_image'] + '">\n' +
'<a class="goods-img-del-x goods-image-del">x</a>'
$('.master-image').html(html);
}
//渲染商品视频
if(goods_info['base']['abs_video']){
like.videoCallback([goods_info['base']['abs_video']]);
}
//渲染商品主图
if(goods_info['base']['poster']){
var html = '' +
'<input name="poster" type="hidden" value="' + goods_info['base']['poster'] + '">' +
' <img class="goods-img" src="' + goods_info['base']['abs_poster'] + '">\n' +
'<a class="goods-img-del-x goods-image-del">x</a>'
$('.poster-image').html(html);
$('.poster-upload').hide();
}
form.render();
switchSpecType(goods_info['base']['spec_type']);
if(goods_info['base']['spec_type'] == 1){
var template_goods_image = $('#template-goods-image').html();
if(goods_info['item'][0]['image']){
$('.goods-one-spec-img-add').parent().html('<input name="one_spec_image" type="hidden" value="' + goods_info['item'][0]['image'] + '"><a class="goods-one-spec-img-del-x">x</a><img class="goods-spec-img" src="' + goods_info['item'][0]['abs_image'] + '">');
}
$('input[name="one_price"]').val(goods_info['item'][0]['price']);
$('input[name="one_cost_price"]').val(goods_info['item'][0]['cost_price']);
$('input[name="one_market_price"]').val(goods_info['item'][0]['market_price']);
$('input[name="one_stock"]').val(goods_info['item'][0]['stock']);
$('input[name="one_volume"]').val(goods_info['item'][0]['volume']);
$('input[name="one_weight"]').val(goods_info['item'][0]['weight']);
$('input[name="one_bar_code"]').val(goods_info['item'][0]['bar_code']);
}
if(goods_info['base']['spec_type'] == 2) {
for(var i in goods_info['spec']){
addSpec(goods_info['spec'][i]['name']);
var spes_values = goods_info['spec'][i]['values'];
for(var j in spes_values){
addSpecvalue($('.add-spec-value').eq(i),spes_values[j]['value'],spes_values[j]['id']);
}
}
for(var i in goods_info['spec']){
$('input[name="spec_id[]"]').eq(i).val(goods_info['spec'][i]['id']);
}
specValueLater();
createTableBySepc();
for(var i in goods_info['item']){
$('#more-spec-lists-table tbody tr').each(function() {
var spec_value_str = $(this).find('input[name="spec_value_str[]"]').first().val();
if(spec_value_str == goods_info['item'][i]['spec_value_str']){
spec_value_temp_ids = $(this).attr('spec-value-temp-ids');
spec_table_data["spec_image[]"+spec_value_temp_ids] = goods_info['item'][i]['abs_image'];
spec_table_data["price[]"+spec_value_temp_ids] = goods_info['item'][i]['price'];
spec_table_data["cost_price[]"+spec_value_temp_ids] = goods_info['item'][i]['cost_price'];
spec_table_data["market_price[]"+spec_value_temp_ids] = goods_info['item'][i]['market_price'];
spec_table_data["stock[]"+spec_value_temp_ids] = goods_info['item'][i]['stock'];
spec_table_data["volume[]"+spec_value_temp_ids] = goods_info['item'][i]['volume'];
spec_table_data["weight[]"+spec_value_temp_ids] = goods_info['item'][i]['weight'];
spec_table_data["bar_code[]"+spec_value_temp_ids] = goods_info['item'][i]['bar_code'];
spec_table_data["item_id[]"+spec_value_temp_ids] = goods_info['item'][i]['id'];
spec_table_data["spec_value_str[]"+spec_value_temp_ids] = goods_info['item'][i]['spec_value_str'];
return false;
}
});
}
setTableValue();
}
likeedit.setContent(likeedit_index,goods_info['base']['content']);
form.render();
{/notempty}
//------------------------编辑页面----------------------------------
});
</script>

View File

@@ -0,0 +1,192 @@
{layout name="layout2" /}
<link rel="stylesheet" href="/static/admin/css/goods.css" media="all">
<div class="">
<div class="layui-tab-content layui-form">
<div class="layui-tab-item goods-content layui-show">
<div class="layui-card-body" pad15>
<div lay-filter="">
<input type="hidden" name="id" value="{$moment.id|default=''}">
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>标题:</label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="custom_required" lay-verType="tips"
autocomplete="off"
switch-tab="0" verify-msg="请输入标题" placeholder="请输入标题"
value="{$moment.title|default=''}"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>员工:</label>
<div class="layui-input-block">
<select name="sid" lay-verType="" placeholder="请选择员工" switch-tab="0" verify-msg="请选择员工">
<option value="">请选择员工</option>
{foreach $staff as $key=>$vo}
<option value="{$vo.id}" {if $moment.sid == $vo.id}selected{/if}>{$vo.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>产品:</label>
<div class="layui-input-block">
<select name="goods_id" lay-verType="" placeholder="请选择关联产品" switch-tab="0" verify-msg="请选择关联产品">
<option value="">请选择关联产品</option>
{foreach $goods as $key=>$vo}
<option value="{$vo.id}" {if $moment.goods_id == $vo.id}selected{/if}>{$vo.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">显示状态:</label>
<div class="layui-input-inline">
<input type="radio" name="state" value="1" title="显示" {if $moment.state == 1}checked{/if}>
<input type="radio" name="state" value="2" title="不显示" {if $moment.state == 2}checked{/if}>
</div>
</div>
<div class="layui-form-item" style="margin-bottom: 0px">
<label class="layui-form-label"><span class="form-label-asterisk">*</span>图片:</label>
<div style="min-height:80px;">
<ul style="list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 10px;">
{notempty name='images'}
{foreach $images as $key=>$img}
<li class="goods-li">
<input name="goods_image[]" type="hidden" value="{$imagePaths[$key]}">
<img class="goods-img goods_image" src="{$img}">
<a class="goods-img-del-x" style="display: none;">x</a>
</li>
{/foreach}
{/notempty}
</ul>
<div class="goods-img-add" lay-verify="goods_image" lay-verType="tips" switch-tab="0" verify-msg="选择图片" style="margin-top: 10px;"></div>
</div>
<br>
</div>
<div class="layui-form-item">
<label class="layui-form-label">内容详情</label>
<div class="layui-input-block">
<textarea name="content" id="content" lay-verify="content" class="field-content">{$moment.content|default=''|raw}</textarea>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item layui-hide">
<input type="button" lay-submit lay-filter="edit-submit" id="edit-submit" value="确认">
</div>
</div>
</div>
<!---------------------------------html模板---------------------------------->
<script type="text/html" id="template-goods-image">
<li class="goods-li">
<input name="goods_image[]" type="hidden" value="{image-src}">
<img class="goods-img goods_image" src="{image-src}">
<a class="goods-img-del-x" style="display: none;">x</a></li>
</script>
<script type="text/javascript">
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['index', 'element', 'form', 'like', 'likeedit'], function () {
var $ = layui.$;
var like = layui.like;
var form = layui.form;
var likeedit = layui.likeedit;
//------------------------基本信息----------------------------------
//图片事件
$(document).on('mouseenter', '.goods-li', function () {
$(this).children().last().show();
});
$(document).on('mouseleave', '.goods-li', function () {
$(this).children().last().hide();
});
$(document).on('click', '.goods-li', function () {
var src = $(this).children('img').attr('src');
like.showImg(src,600);
});
$(document).on('click', '.goods-img-del-x', function () {
$(this).parent().remove();
return false;
});
like.imageUpload('.goods-img-add', function (uris, element) {
// 多图片上传逻辑最多8张
var count = element.prev().children().length;
count = !count ? 0 : count;
if (count + uris.length > 8) {
layer.msg('最多只能选中8张图片');
return;
}
uris = uris.reverse();
for(var i in uris){
var uri = uris[i];
if (!uri) continue; // 跳过无效的 uri
var template_goods_image = $('#template-goods-image').html();
if (!template_goods_image) {
console.error('模板 template-goods-image 不存在');
continue;
}
// 获取文件路径(用于 hidden input
var filePath = like.getUrlFileName ? like.getUrlFileName(uri, '{$storageUrl}') : uri;
if (!filePath) {
filePath = uri; // 如果 getUrlFileName 返回 undefined使用原始 uri
}
// 先替换 input 中的 {image-src},再替换 img 中的 {image-src}
var html = template_goods_image.replace('{image-src}', filePath);
html = html.replace('{image-src}', uri);
element.prev().append(html);
}
}, true);
likeedit.set({
uploadImage: {
url: "{:url('file/image')}",
type: 'post'
}
});
var likeedit_index = likeedit.build('content'); //建立编辑器
// 设置编辑器内容(编辑模式)
{notempty name='moment.content'}
likeedit.setContent(likeedit_index, '{$moment.content|raw}');
{/notempty}
form.verify({
content: function () {
likeedit.sync(likeedit_index)
},
custom_required: function (value, item) {
if (!value) {
return $(item).attr('verify-msg');
}
},
goods_image: function (value, item) {
// 检查是否有上传的图片
var imageCount = $('input[name="goods_image[]"]').filter(function() {
return $(this).val() != '';
}).length;
if (imageCount == 0) {
return $(item).attr('verify-msg');
}
}
});
form.render();
});
</script>

View File

@@ -0,0 +1,132 @@
{layout name="layout2" /}
<link rel="stylesheet" href="/static/admin/css/goods.css" media="all">
<div class="">
<div class="layui-card">
<div class="layui-card-body" pad15>
<div class="layui-form" lay-filter="">
<div class="layui-form-item">
<label class="layui-form-label">标题:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$moment.title|default='--'}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">绑定员工:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$staff.name|default='--'}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">绑定商品:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$goods.name|default='--'}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">显示状态:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{if $moment.state == 1}
<span class="layui-badge layui-bg-green">显示</span>
{else /}
<span class="layui-badge layui-bg-gray">不显示</span>
{/if}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">图片:</label>
<div class="layui-input-block">
{notempty name='images'}
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
{foreach $images as $img}
<div style="position: relative; width: 120px; height: 120px; border: 1px solid #e6e6e6; cursor: pointer;" class="image-item">
<img src="{$img}" alt="图片" style="width: 100%; height: 100%; object-fit: cover;">
</div>
{/foreach}
</div>
{else /}
<span style="color: #999;">无图片</span>
{/notempty}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">内容详情:</label>
<div class="layui-input-block">
<div style="border: 1px solid #e6e6e6; padding: 15px; min-height: 200px; background: #fafafa;">
{$moment.content|raw|default='--'}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">收藏数量:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$moment.collection|default=0}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">点赞数量:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$moment.like|default=0}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">浏览量:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$moment.page_views|default=0}
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">创建时间:</label>
<div class="layui-input-block">
<div class="layui-input" style="border: none; background: #f5f5f5;">
{$moment.create_time|default='--'}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
layui.use(['layer'], function(){
var layer = layui.layer;
// 点击图片放大查看
$('.image-item').on('click', function(){
var src = $(this).find('img').attr('src');
layer.open({
type: 1,
title: false,
closeBtn: 0,
area: 'auto',
skin: 'layui-layer-nobg',
shadeClose: true,
content: '<img src="' + src + '" style="max-width: 90vw; max-height: 90vh;">'
});
});
});
</script>

View File

@@ -0,0 +1,469 @@
{layout name="layout1" /}
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-collapse like-layui-collapse" lay-accordion="" style="border:1px dashed #c4c4c4">
<div class="layui-colla-item">
<h2 class="layui-colla-title like-layui-colla-title" style="background-color: #fff">操作提示</h2>
<div class="layui-colla-content layui-show">
<p>*服务列表,对服务号进行编辑,调整和资料查看等操作。</p>
</div>
</div>
</div>
</div>
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 200px;">
<input type="text" id="keyword" name="keyword" placeholder="请输入关键词" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<button class="layui-btn layui-btn-sm layuiadmin-btn-user {$view_theme_color}" lay-submit lay-filter="user-search">查询</button>
<button class="layui-btn layui-btn-sm layuiadmin-btn-user layui-btn-primary " lay-submit lay-filter="user-clear-search">清空查询</button>
<button class="layui-btn layui-btn-sm layuiadmin-btn-user layui-btn-primary " lay-submit lay-filter="export-file">导出</button>
<button class="layui-btn layui-btn-sm layuiadmin-btn-user {$view_theme_color}" data-type="send_coupon">添加</button>
</div>
</div>
</div>
<div class="layui-card-body">
<table id="user-lists" lay-filter="user-lists"></table>
<script type="text/html" id="user-operation">
<a class="layui-btn layui-btn-primary layui-btn-sm" lay-event="info">资料</a>
<a class="layui-btn layui-btn-normal layui-btn-sm" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-normal layui-btn-sm" lay-event="del">删除</a>
</script>
<script type="text/html" id="user-info">
{{ d.title }}
</script>
<script type="text/html" id="referrer">
{{ d.staff_name || '--' }}
</script>
<script type="text/html" id="account">
{{ d.collection || 0 }}
</script>
<script type="text/html" id="status">
{{# if(d.state == 1){ }}
<span class="layui-badge layui-bg-green">显示</span>
{{# } else { }}
<span class="layui-badge layui-bg-gray">不显示</span>
{{# } }}
</script>
<script type="text/html" id="img-templet">
{{# if(d.img){ }}
<img src="{{ d.img }}" alt="封面图" style="width: 60px; height: 60px; object-fit: cover; cursor: pointer;" class="image-show">
{{# } else { }}
<span>无图片</span>
{{# } }}
</script>
</div>
</div>
</div>
<style>
.layui-table-cell {
height: auto;
}
</style>
<script>
layui.config({
version:"{$front_version}",
base: '/static/plug/layui-admin/dist/layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['index','table','like','laydate'], function(){
var $ = layui.$
,form = layui.form
,table = layui.table
,like = layui.like
, laydate = layui.laydate;
//监听搜索
form.on('submit(user-search)', function(data){
var field = data.field;
//执行重载
table.reload('user-lists', {
where: field,
page: {
curr: 1 //重新从第 1 页开始
}
});
});
//日期时间范围
laydate.render({
elem: '#start_time'
,type: 'datetime'
,trigger: 'click'
});
//日期时间范围
laydate.render({
elem: '#end_time'
,type: 'datetime'
,trigger: 'click'
});
//日期时间范围
laydate.render({
elem: '#start_times'
,type: 'datetime'
,trigger: 'click'
});
//日期时间范围
laydate.render({
elem: '#end_times'
,type: 'datetime'
,trigger: 'click'
});
//清空查询
form.on('submit(user-clear-search)', function(){
$('#keyword_type').val('sn');
$('#keyword').val(''); //清空输入框
$('#level').val(''); //清空输入框
$('#group_id').val(''); //清空输入框
$('#start_time').val(''); //清空输入框
$('#end_time').val(''); //清空输入框
form.render('select');
//刷新列表
table.reload('user-lists', {
where: [],
page: {
curr: 1 //重新从第 1 页开始
}
});
});
// 导出
form.on('submit(export-file)', function(data){
var field = data.field;
$.ajax({
url: '{:url("user/exportFile")}',
type: 'get',
data: field,
dataType: 'json',
error: function() {
layer.msg('导出超时,请稍后再试!');
},
success: function(res) {
table.exportFile(res.data.exportTitle,res.data.exportData, res.data.exportExt, res.data.exportName);
},
timeout: 15000
});
layer.msg('导出中请耐心等待~');
});
//事件
var active = {
send_coupon:function() { //发放优惠券
layer.open({
type: 2
,title: '添加'
,content: '{:url("Moments/add")}'
,area: ['55%', '80%']
,btn: ['确定', '取消']
,yes: function(index, layero){
var iframeWindow = window['layui-layer-iframe'+ index]
,submitID = 'user_group-submit'
,submit = layero.find('iframe').contents().find("#add-user_group-submit");
//监听提交
iframeWindow.layui.form.on('submit(add-user_group-submit)', function(data){
var field = data.field;
// 手动收集图片数组字段,确保数据正确传递
var goods_images = [];
iframeWindow.$('input[name="goods_image[]"]').each(function(){
var val = iframeWindow.$(this).val();
if (val && val.trim() !== '') {
goods_images.push(val.trim());
}
});
// 将图片数组添加到提交数据中
if (goods_images.length > 0) {
field['goods_image'] = goods_images;
}
// 阻止表单默认提交,使用 AJAX 提交
like.ajax({
url:'{:url("Moments/add")}',
data:field,
type:"post",
success:function(res)
{
if(res.code == 1)
{
layui.layer.msg(res.msg, {
offset: '15px'
, icon: 1
, time: 1000
});
layer.close(index); //关闭弹层
table.reload('user-lists'); //数据刷新
} else {
// 验证失败,显示错误信息,但不关闭弹窗,保留已上传的图片
layui.layer.msg(res.msg || '提交失败,请检查表单信息', {
offset: '15px'
, icon: 2
, time: 2000
});
}
},
error: function(xhr, status, error) {
// 请求失败,显示错误信息,但不关闭弹窗
layui.layer.msg('提交失败:' + (xhr.responseJSON?.msg || error), {
offset: '15px'
, icon: 2
, time: 2000
});
}
});
return false; // 阻止表单默认提交
});
submit.trigger('click');
}
});
}
,set_group:function() { //设置分组
var check_status = table.checkStatus('user-lists')
,user_list = check_status.data; //得到选中的数据
//是否已选数据
if( user_list.length === 0){
return layer.msg('请选择用户');
}
//获取所选id
ids = [];
for (var i in user_list){
ids.push(user_list[i]['id']);
}
layer.open({
type: 2
,title: '设置分组'
,content: '{:url("user/setGroup")}'
,area: ['60%', '60%']
,btn: ['确定', '取消']
,yes: function(index, layero){
var iframeWindow = window['layui-layer-iframe'+ index]
,submitID = 'user-submit-edit'
,submit = layero.find('iframe').contents().find("#set_group-submit");
//监听提交
iframeWindow.layui.form.on('submit(set_group-submit)', function(data){
var group_id = data.field.group_id;
$.ajax({
url:'{:url("user/setGroup")}',
data:{group_id:group_id,user_ids:ids},
type:"post",
success:function(res)
{
if(res.code == 1)
{
layui.layer.msg(res.msg, {
offset: '15px'
, icon: 1
, time: 1000
});
layer.close(index); //关闭弹层
table.reload('user-lists'); //数据刷新
} else if(res.code == 1){
layui.layer.msg(res.msg)
}
}
});
});
submit.trigger('click');
}
});
}
}
$('.layui-btn.layuiadmin-btn-user').on('click', function(){
var type = $(this).data('type');
active[type] ? active[type].call(this) : '';
});
$(document).on('click', '.image-show', function () {
var src = $(this).attr('src');
like.showImg(src,600);
});
});
layui.define(['table', 'form'], function(exports){
var $ = layui.$
,table = layui.table
,form = layui.form;
//管理员管理
table.render({
id:'user-lists'
,elem: '#user-lists'
,url: '{:url("moments/lists")}' //模拟接口
,cols: [[
{type: 'checkbox'}
,{field: 'id', title: 'ID', }
,{field: 'title', title: '标题',align: 'center', toolbar: '#user-info'}
,{field: 'goods_name', title: '绑定商品',}
,{field: 'staff_name', title: '绑定员工',align: 'center',toolbar: '#referrer'}
,{field: 'img', title: '封面图', templet: '#img-templet'}
,{field: 'collection', title: '收藏数量',toolbar: '#account'}
,{field: 'like', title: '点赞数量'}
,{field: 'page_views', title: '浏览量'}
,{field: 'order_sn', title: '订单编号'}
,{field: 'state', title: '状态',align: 'center',templet: '#status'}
,{fixed: 'right', title: '操作', width: 360,toolbar: '#user-operation'}
]]
,page:true
,text: {none: '暂无数据!'}
,parseData: function(res){ //将原始数据解析成 table 组件所规定的数据
return {
"code":res.code,
"msg":res.msg,
"count": res.data.count, //解析数据长度
"data": res.data.lists, //解析数据列表
};
}
, done: function fix() {
$(".layui-table-main tr").each(function (index, val) {
$(".layui-table-fixed").each(function () {
$($(this).find(".layui-table-body tbody tr")[index]).height($(val).height());
});
});
$(".layui-table-header tr").each(function (index, val) {
$(".layui-table-fixed").each(function () {
$($(this).find(".layui-table-header thead tr")[index]).height($(val).height());
});
});
window.onresize = function () {
fix()
}
}
});
var moreShow = 0;
//监听工具条
table.on('tool(user-lists)', function(obj){
var data = obj.data;
// 查看详情
if(obj.event === 'info'){
var id = data.id;
layer.open({
type: 2
,title: '查看详情'
,content: '{:url("moments/info")}?id='+id
,area: ['90%','90%']
,btn: ['返回']
,yes: function(index){
layer.close(index);
}
});
}
// 编辑
if(obj.event === 'edit'){
var id = data.id;
layer.open({
type: 2
,title: '编辑动态'
,content: '{:url("moments/edit")}?id='+id
,area: ['90%', '90%']
,btn: ['确定', '取消']
,yes: function(index, layero){
var iframeWindow = window['layui-layer-iframe'+ index]
,submit = layero.find('iframe').contents().find('#edit-submit');
//监听提交
iframeWindow.layui.form.on('submit(edit-submit)', function(formData){
var field = formData.field;
// 手动收集图片数组
var goods_images = [];
layero.find('iframe').contents().find('input[name="goods_image[]"]').each(function(){
var imgVal = $(this).val();
if (imgVal) {
goods_images.push(imgVal);
}
});
field.goods_image = goods_images;
$.ajax({
url:'{:url("moments/edit")}',
data:field,
type:"post",
success:function(res)
{
if(res.code == 1)
{
layui.layer.msg(res.msg, {
offset: '15px'
, icon: 1
, time: 1000
});
layer.close(index); //关闭弹层
table.reload('user-lists'); //数据刷新
}else{
layer.msg(res.msg, {
offset: '15px'
, icon: 2
, time: 1000
});
}
}
});
});
submit.trigger('click');
}
});
}
// 删除
if(obj.event === 'del'){
var id = data.id;
layer.confirm('确定要删除这条动态吗?', {icon: 3, title:'提示'}, function(index){
$.ajax({
url: '{:url("moments/del")}',
type: 'post',
data: {id: id},
success: function(res){
if(res.code == 1){
layer.msg(res.msg, {icon: 1, time: 1000}, function(){
table.reload('user-lists'); //数据刷新
});
} else {
layer.msg(res.msg, {icon: 2, time: 1000});
}
layer.close(index);
}
});
});
}
if(obj.event === 'staff'){
var id = obj.data.id;
$.post('{:url("user/staff")}?id='+id,function(res){
if(res.code==0){
layer.msg(res.msg, {icon: 1, time: 1000}, function(){
layer.msg(res.msg,{icon: 6, time: 1000});
layer.close(index); //关闭弹层
table.reload('user-lists'); //数据刷新
});
}else{
layer.msg(res.msg)
}
})
}
});
});
</script>

View File

@@ -101,7 +101,7 @@
<div class="layui-form-item">
<label class="layui-form-label">订单类型:</label>
<div class="layui-input-block">
<input type="radio" name="order_ysck" title="已收款" value=""0 lay-filter="link" >
<input type="radio" name="order_ysck" title="已收款" value="0" lay-filter="link" >
<input type="radio" name="order_ysck" title="应收款" value="1" lay-filter="link" checked="turn">
</div>
</div>
@@ -186,9 +186,12 @@
var value = data.value;
$('.link').hide();
switch (value) {
case '1':
case '0': // 已收款时显示选择付款
$('.page').show();
break;
case '1': // 应收款时隐藏
$('.page').hide();
break;
case '2':
$('.page').show();
$('.url-tips').show();
@@ -196,7 +199,6 @@
case '3':
$('.url').show();
$('.url-tips').show();
break;
}

View File

@@ -298,43 +298,86 @@
setSelectFirst();
function setSelectFirst(default_id) {
console.log('setSelectFirst 被调用default_id:', default_id);
var category_select_html = '<option value="">请选择分类</option>';
var foundMatch = false;
for (var i in categorys) {
if (categorys[i]['level'] == 1) {
category_select_html += '<option value="' + categorys[i]['id'] + '">' + categorys[i]['name'] + '</option>';
var selected = '';
if (default_id && String(categorys[i]['id']) == String(default_id)) {
selected = ' selected';
foundMatch = true;
console.log('找到匹配的省份:', categorys[i]['name'], 'ID:', categorys[i]['id']);
}
category_select_html += '<option value="' + categorys[i]['id'] + '"' + selected + '>' + categorys[i]['name'] + '</option>';
}
}
$('select[name="first_category_id"]').html(category_select_html);
$('select[name="first_category_id"]').val(default_id);
if (default_id) {
$('select[name="first_category_id"]').val(String(default_id));
console.log('设置省份值:', String(default_id), '是否找到匹配:', foundMatch);
}
form.render('select');
}
function setSelectSecond(default_id, parent_id) {
console.log('setSelectSecond 被调用default_id:', default_id, 'parent_id:', parent_id);
parent_id = parent_id === undefined ? $('select[name="first_category_id"]').val() : parent_id;
if (!parent_id) {
console.log('setSelectSecond: parent_id 为空,返回');
return;
}
parent_id = String(parent_id);
$('select[name="second_category_id"]').html('<option value="">请选择分类</option>');
$('select[name="third_category_id"]').html('<option value="">请选择分类</option>');
var category_select_html = '<option value="">请选择分类</option>';
var foundMatch = false;
for (var i in categorys) {
if (categorys[i]['parent_id'] == parent_id) {
category_select_html += '<option value="' + categorys[i]['id'] + '">' + categorys[i]['name'] + '</option>';
if (String(categorys[i]['parent_id']) == parent_id) {
var selected = '';
if (default_id && String(categorys[i]['id']) == String(default_id)) {
selected = ' selected';
foundMatch = true;
console.log('找到匹配的城市:', categorys[i]['name'], 'ID:', categorys[i]['id']);
}
category_select_html += '<option value="' + categorys[i]['id'] + '"' + selected + '>' + categorys[i]['name'] + '</option>';
}
}
$('select[name="second_category_id"]').html(category_select_html);
$('select[name="second_category_id"]').val(default_id);
if (default_id) {
$('select[name="second_category_id"]').val(String(default_id));
console.log('设置城市值:', String(default_id), '是否找到匹配:', foundMatch);
}
form.render('select');
}
function setSelectThird(default_id, parent_id) {
console.log('setSelectThird 被调用default_id:', default_id, 'parent_id:', parent_id);
parent_id = parent_id === undefined ? $('select[name="second_category_id"]').val() : parent_id;
if (!parent_id) {
console.log('setSelectThird: parent_id 为空,返回');
return;
}
parent_id = String(parent_id);
$('select[name="third_category_id"]').html('<option value="">请选择分类</option>');
var first_category_id = $('select[name="first_category_id"]').val();
var category_select_html = '<option value="">请选择分类</option>';
var foundMatch = false;
for (var i in categorys) {
if (categorys[i]['parent_id'] == parent_id) {
category_select_html += '<option value="' + categorys[i]['id'] + '">' + categorys[i]['name'] + '</option>';
if (String(categorys[i]['parent_id']) == parent_id) {
var selected = '';
if (default_id && String(categorys[i]['id']) == String(default_id)) {
selected = ' selected';
foundMatch = true;
console.log('找到匹配的区县:', categorys[i]['name'], 'ID:', categorys[i]['id']);
}
category_select_html += '<option value="' + categorys[i]['id'] + '"' + selected + '>' + categorys[i]['name'] + '</option>';
}
}
$('select[name="third_category_id"]').html(category_select_html);
$('select[name="third_category_id"]').val(default_id);
if (default_id) {
$('select[name="third_category_id"]').val(String(default_id));
console.log('设置区县值:', String(default_id), '是否找到匹配:', foundMatch);
}
form.render('select');
}
@@ -465,7 +508,7 @@
}
{notempty name='info'}
var goods_info= {$info|raw|default=''};
console.log(goods_info);
console.log('订单数据:', goods_info);
$('input[name="id"]').val(goods_info['id']);
$('input[name="address"]').val(goods_info['address']);
$('input[name="total_amount"]').val(goods_info['total_amount']);
@@ -474,9 +517,89 @@
$('input[name="phone"]').val(goods_info['mobile']);
$('input[name="store_latitude"]').val(goods_info['lng']);
$('input[name="store_longitude"]').val(goods_info['lat']);
setSelectFirst(goods_info['province']);
setSelectSecond(goods_info['city']);
setSelectThird(goods_info['district']);
// 设置地区选择器,根据返回的 province、city、district 字段
// 注意需要检查值是否存在包括0的情况0是有效的ID值
var province_id = '';
var city_id = '';
var district_id = '';
// 检查province字段包括0值
if (goods_info['province'] !== undefined && goods_info['province'] !== null && goods_info['province'] !== '') {
province_id = goods_info['province'];
}
// 检查city字段包括0值
if (goods_info['city'] !== undefined && goods_info['city'] !== null && goods_info['city'] !== '') {
city_id = goods_info['city'];
}
// 检查district字段包括0值
if (goods_info['district'] !== undefined && goods_info['district'] !== null && goods_info['district'] !== '') {
district_id = goods_info['district'];
}
console.log('地区数据:', {
province: goods_info['province'],
city: goods_info['city'],
district: goods_info['district'],
province_id: province_id,
city_id: city_id,
district_id: district_id
});
// 初始化地区选择器的函数
function initRegionSelectors() {
if (province_id !== '') {
// 转换为字符串,确保类型匹配
province_id = String(province_id);
// 先设置省份
setSelectFirst(province_id);
// 等待省份选择器渲染完成后再设置城市
setTimeout(function() {
// 验证省份值是否设置成功
var selectedProvince = $('select[name="first_category_id"]').val();
console.log('已选省份ID:', selectedProvince, '期望:', province_id);
if (city_id !== '' && selectedProvince == province_id) {
city_id = String(city_id);
setSelectSecond(city_id, province_id);
// 等待城市选择器渲染完成后再设置区县
setTimeout(function() {
// 验证城市值是否设置成功
var selectedCity = $('select[name="second_category_id"]').val();
console.log('已选城市ID:', selectedCity, '期望:', city_id);
if (district_id !== '' && selectedCity == city_id) {
district_id = String(district_id);
setSelectThird(district_id, city_id);
setTimeout(function() {
var selectedDistrict = $('select[name="third_category_id"]').val();
console.log('已选区县ID:', selectedDistrict, '期望:', district_id);
// 最后重新渲染一次,确保所有选择器都正确显示
form.render('select');
}, 100);
} else {
form.render('select');
}
}, 300);
} else {
form.render('select');
}
}, 300);
} else {
form.render('select');
}
}
// 延迟执行,确保页面完全加载和选择器初始化完成
setTimeout(function() {
console.log('开始初始化地区选择器...');
initRegionSelectors();
}, 200);
setBrand(goods_info['channel_id']); //部门的修改
setBrandss(goods_info['gord_id']); //部门的修改

View File

@@ -73,6 +73,20 @@
height: 62px;
margin-bottom: 5px;
}
/* 退款中状态绿色背景 */
.refunding-row {
background-color: #c8e6c9 !important;
}
.refunding-row:hover {
background-color: #a5d6a7 !important;
}
/* 同意退款后淡绿色背景 */
.order-status-4-row {
background-color: #e8f5e9 !important;
}
.order-status-4-row:hover {
background-color: #c8e6c9 !important;
}
</style>
<div class="layui-fluid">
@@ -199,6 +213,7 @@
<li data-type="1">已支付</li>
<li data-type="2">已退款</li>
<li data-type="3">拒退款</li>
<li data-type="66">已删除</li>
</ul>
<div class="layui-tab-item layui-show">
@@ -331,7 +346,11 @@
<a class="layui-btn-primary layui-btn-sm" lay-event="channel" style="display: inline-block;">渠道</a>
<a class="layui-btn layui-btn-primary layui-btn-sm operation-btn" lay-event="remarks">备注</a>
<a class="layui-btn-primary layui-btn-sm" style="display: inline-block;" lay-event="allocation"> 分配</a>
{{# if(d.del == 0){ }}
<a class="layui-btn layui-btn-primary layui-btn-sm operation-btn" style="display: inline-block;background-color:#fd4634;color:#fff" lay-event="cancel">删除</a>
{{# } else { }}
<a class="layui-btn layui-btn-primary layui-btn-sm operation-btn" style="display: inline-block;background-color:#1E9FFF;color:#fff" lay-event="recover">恢复</a>
{{# } }}
{{# if(d.order_status == 1){ }}
<a class="layui-btn-primary layui-btn-sm" style="display: inline-block;" lay-event="delivery">发货</a>
{{# } }}
@@ -356,18 +375,46 @@
<div style="text-align: left">
<p>订单编号:{{d.order_sn}}</p>
<p>下单时间:{{d.create_time}}</p>
<p>订单来源:{{d.channel}}</p>
<p>收款方式{{d.gord_name}}</p>
<p>订单来源:
{{# if(d.channel){ }}
{{d.channel}}
{{# } else { }}
小程序
{{# } }}
</p>
<p>支付方式
{{# if(d.pay_way == 1){ }}
微信支付
{{# } }}
{{# if(d.pay_way == 2){ }}
支付宝支付
{{# } }}
{{# if(d.pay_way == 3){ }}
余额支付
{{# } }}
{{# if(!d.pay_way || (d.pay_way != 1 && d.pay_way != 2 && d.pay_way != 3)){ }}
-
{{# } }}
</p>
{{# if(d.pay_zd == 0){ }}
<p style="color:#fe0707">支付状态
已收款
{{# if(d.pay_status == 0 || d.pay_zd == 1){ }}
<p style="color:#fe0707">订单状态
待支付
</p>
{{# } }}
{{# if(d.pay_zd == 1){ }}
<p style="color:#3A91FB">支付状态
未收账款
{{# if(d.pay_status == 1 && d.pay_zd == 0){ }}
<p style="color:#3A91FB">订单状态
已支付
</p>
{{# } }}
{{# if(d.pay_status == 2){ }}
<p style="color:#ff9800">订单状态
已退款
</p>
{{# } }}
{{# if(d.pay_status == 3){ }}
<p style="color:#f44336">订单状态
拒绝退款
</p>
{{# } }}
</div>
@@ -582,6 +629,7 @@
, {field: 'order_goods', title: '商品信息', align: 'center',templet:'#goods',width:320}
, {field: 'total_amount', title: '支付信息', align: 'center',templet:'#amount',width:180}
, {field: 'admin_id', title: '订单归属人', align: 'center',width:180}
, {field: 'order_remarks', title: '备注信息', align: 'center',width:130}
, {fixed: 'right', title: '操作', width: 160, align: 'center', toolbar: '#order-operation'}
]]
, page: true
@@ -600,6 +648,40 @@
$($(".layui-table-fixed-l .layui-table-body tbody tr")[index]).height($(val).height());
$($(".layui-table-fixed-r .layui-table-body tbody tr")[index]).height($(val).height());
});
// 如果 order_status == 4同意退款整行淡绿色显示
// 如果 refund_status > 0 但 order_status != 4退款中整行绿色显示
if (res && res.data && res.data.length > 0) {
res.data.forEach(function(item, index) {
var orderStatus = item.order_status ? parseInt(item.order_status) : 0;
var refundStatus = item.refund_status ? parseInt(item.refund_status) : 0;
// 同意退款后order_status == 4显示淡绿色
if (orderStatus == 4) {
// 给主表格行添加淡绿色背景
var $mainRow = $(".layui-table-main tbody tr").eq(index);
$mainRow.addClass('order-status-4-row');
// 给固定列的行也添加淡绿色背景
var $fixedLeftRow = $(".layui-table-fixed-l .layui-table-body tbody tr").eq(index);
var $fixedRightRow = $(".layui-table-fixed-r .layui-table-body tbody tr").eq(index);
$fixedLeftRow.addClass('order-status-4-row');
$fixedRightRow.addClass('order-status-4-row');
}
// 退款中refund_status > 0 但 order_status != 4显示绿色
else if (refundStatus > 0) {
// 给主表格行添加绿色背景
var $mainRow = $(".layui-table-main tbody tr").eq(index);
$mainRow.addClass('refunding-row');
// 给固定列的行也添加绿色背景
var $fixedLeftRow = $(".layui-table-fixed-l .layui-table-body tbody tr").eq(index);
var $fixedRightRow = $(".layui-table-fixed-r .layui-table-body tbody tr").eq(index);
$fixedLeftRow.addClass('refunding-row');
$fixedRightRow.addClass('refunding-row');
}
});
}
}
});
}
@@ -782,7 +864,7 @@
});
}
//取消订单
// 退款订单
if(obj.event === 'cancelss'){
layer.confirm('确认订单退款吗?', {
btn: ['确认','取消'] //按钮
@@ -807,7 +889,7 @@
}
//取消订单
// 删除订单逻辑删除del 设置为 1
if(obj.event === 'cancel'){
layer.confirm('确定删除订单的数据?', {
btn: ['确认','取消'] //按钮
@@ -829,6 +911,35 @@
});
}
// 恢复订单del 设置为 0
if(obj.event === 'recover'){
layer.confirm('确定恢复该订单吗?', {
btn: ['确认','取消'] //按钮
}, function(){
like.ajax({
url: '{:url("order/recover")}?id='+id
, type: 'get'
, success: function (res) {
if (res.code == 1) {
layui.layer.msg(res.msg, {
offset: '15px'
, icon: 1
, time: 1000
},function () {
table.reload('order-lists');
});
} else {
layui.layer.msg(res.msg || '恢复失败', {
offset: '15px'
, icon: 2
, time: 1500
});
}
},
});
});
}
//商家备注
if(obj.event === 'remarks') {
like.ajax({

View File

@@ -48,7 +48,8 @@
</div>
</div>
</div>
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG"></script>
<!--<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG"></script>-->
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=66NBZ-KH3W4-7QSUS-K5OBA-7P552-F7F6T"></script>
<script type="text/javascript">

View File

@@ -90,7 +90,9 @@
<button class="layui-btn layui-btn-sm layuiadmin-btn-ad layui-btn-primary " lay-submit
lay-filter="user-clear-search">清空查询
</button>
<button class="layui-btn layui-btn-sm layuiadmin-btn-ad layui-btn-normal" lay-submit
lay-filter="export-file">导出
</button>
</div>
</div>
@@ -161,7 +163,7 @@
form.on('submit(export-file)', function(data){
var field = data.field;
$.ajax({
url: '{:url("order/exportFile")}?type=' + listType,
url: '{:url("staff/exportWages")}',
type: 'get',
data: field,
dataType: 'json',
@@ -171,7 +173,7 @@
success: function(res) {
table.exportFile(res.data.exportTitle,res.data.exportData, res.data.exportExt, res.data.exportName);
},
timeout: 15000
timeout: 60000
});
layer.msg('导出中请耐心等待~');
});

View File

@@ -144,7 +144,8 @@
</div>
</div>
</div>
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG"></script>
<!--<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG"></script>-->
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=66NBZ-KH3W4-7QSUS-K5OBA-7P552-F7F6T"></script>
<script type="text/javascript">
//注意:选项卡 依赖 element 模块,否则无法进行功能性操作

View File

@@ -25,7 +25,7 @@ use think\Request;
class AppEnd
{
/**
* 记录统计信息(用户访问量)
* 记录统计信息(用户访问量)1212
* @param Request $request
*/
public function run(Request $request)

View File

@@ -5,10 +5,16 @@ namespace app\api\controller;
use app\api\logic\AdLogic;
use app\common\server\UrlServer;
use think\Db;
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
class Ad extends ApiBase
{
public $like_not_need_login = ['lists','channel','label','add_comost','add_comost','list_comost','follow_comost','comost_add','label_edit','comost_info','notice','position','position_list','vode_type','video_list','video_info','user_wages','user_wages_add','user_leave','fine','recruit','last_leave','last_fine','notice_list','leave','auth'];
public $like_not_need_login = ['lists','popup','channel','label','add_comost','add_comost','list_comost','follow_comost','comost_add','label_edit','comost_info','notice','position','position_list','vode_type','video_list','video_info','user_wages','user_wages_add','user_leave','fine','recruit','last_leave','last_fine','notice_list','leave','auth'];
/**
* @return void
@@ -26,6 +32,21 @@ class Ad extends ApiBase
$this->_success('获取成功', $list);
}
//获取弹窗
public function popup()
{
$list = Db::name('ad')
->where('status',1)
->where('pid',25)
->field('id,name,image,link_type,link')
->find();
if ($list != null){
$list['image'] = UrlServer::getFileUrl($list['image']);
}
$this->_success('获取成功', $list);
}
//获取客户的渠道
public function channel(){
$list=Db::name('staffchannel')->field('id,name')->select();

View File

@@ -26,7 +26,7 @@ use app\api\logic\AfterSaleLogic;
* Class Order
* @package app\api\controller
*/
class AfterSale extends ApiBase
class Aftersale extends ApiBase
{
public function lists()
{
@@ -107,7 +107,10 @@ class AfterSale extends ApiBase
$this->_error('订单已完成,无法申请退款');
}
$ref=Db::name('order')->where('id',$post['id'])->update(['refund_status'=>2]);
$this->_success('申请成功');
if ($ref){
$this->_success('申请成功');
}else{
$this->_error('申请失败');
}
}
}

View File

@@ -26,6 +26,10 @@ use think\facade\Config;
use think\facade\Debug;
use think\Response;
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
class ApiBase extends Controller
{
public $user_info = [];

View File

@@ -0,0 +1,89 @@
<?php
namespace app\api\controller;
use think\Db;
class Calendar extends ApiBase
{
public function index()
{
$month = $this->request->post("month");
if (!$month) {
return $this->_error("月份不能为空");
}
// 验证月份格式2026-01
if (!preg_match('/^\d{4}-\d{2}$/', $month)) {
return $this->_error("月份格式错误请使用格式2026-01");
}
// 计算该月的开始和结束日期
$startDate = $month . '-01';
$endDate = date('Y-m-t', strtotime($startDate)); // 获取该月最后一天
$uid = $this->user_id;
// 查询该月份的所有服务数据,关联总订单表、商品表和员工表
$service = Db::name('order_exe')
->alias('oe')
->join('order o', 'oe.order_sn = o.order_sn', 'INNER')
->join('goods g', 'oe.goods_id = g.id', 'LEFT')
->join('staff s', 'oe.staff_id = s.id', 'LEFT')
->where('o.user_id', $uid)
->where('oe.date', '>=', $startDate)
->where('oe.date', '<=', $endDate)
->where('oe.abnormal', 0)
->field('oe.staff_status,oe.date,oe.addtime,oe.start_time,oe.timeout,oe.xq,oe.staff_id,oe.order_sn,s.name as staff_name,s.onwork,s.leave,g.name as goods_name')
->order('oe.date asc, oe.addtime asc')
->select();
$serviceDate = [];
if ($service) {
foreach ($service as $v) {
// 如果有绑定员工,但员工状态不符合要求,则跳过
// 注释掉员工状态过滤,先看看是否能查询出来
// if (!empty($v['staff_id']) && ($v['onwork'] != 1 || $v['leave'] != 1)) {
// continue;
// }
// 处理服务时间
$timeRange = '';
if (!empty($v['start_time']) && !empty($v['timeout'])) {
// 判断是否为时间戳(数字)
$startTimestamp = is_numeric($v['start_time']) ? $v['start_time'] : strtotime($v['start_time']);
$timeoutTimestamp = is_numeric($v['timeout']) ? $v['timeout'] : strtotime($v['timeout']);
$startTime = date('H:i', $startTimestamp);
$timeoutTime = date('H:i', $timeoutTimestamp);
$timeRange = $startTime . '-' . $timeoutTime;
} elseif (!empty($v['start_time'])) {
$startTimestamp = is_numeric($v['start_time']) ? $v['start_time'] : strtotime($v['start_time']);
$startTime = date('H:i', $startTimestamp);
$timeRange = $startTime;
}
$serviceDate[] = [
'staff_status' => $v['staff_status'],
'date' => $v['date'],
'staff_name' => $v['staff_name'] ?? '',
'goods_name' => $v['goods_name'] ?? '',
'addtime' => $v['addtime'] == 1 ? '上午' : ($v['addtime'] == 2 ? '下午' : ''), //1表示上午2表示下午
'serviceTime' => $timeRange,
'xq' => $v['xq'] ?? $this->getChineseWeekDay($v['date']), // 如果没有xq字段则根据日期计算
];
}
}
return $this->_success("成功", $serviceDate);
}
/**
* 获取中文星期
* @param string $date 日期格式Y-m-d
* @return string
*/
private function getChineseWeekDay($date)
{
$weekDay = date('w', strtotime($date));
$weekList = ['日', '一', '二', '三', '四', '五', '六'];
return '星期' . $weekList[$weekDay];
}
}

View File

@@ -42,7 +42,7 @@ class Cart extends ApiBase
if (true !== $check) {
$this->_error($check);
}
$res = CartLogic::add($post['item_id'], $post['goods_num'], $this->user_id);
$res = CartLogic::add($post['item_id'], $post['goods_num'], $this->user_id, $post['type']);
if ($res === true) {
$this->_success('加入成功');
}

View File

@@ -3,6 +3,7 @@
namespace app\api\controller;
use app\api\server\CityService;
use app\api\model\User;
class City extends ApiBase
{
@@ -23,6 +24,39 @@ class City extends ApiBase
public function index()
{
$array = $this->cityService->list();
return $this->_success($array);
return $this->_success('成功',$array);
}
/**
* 根据经纬度获取所在城市
* @return void
*/
public function getCity()
{
$lat = $this->request->param('lat', '');
$lng = $this->request->param('lng', '');
if (empty($lat) || empty($lng)) {
return $this->_error('请提供经纬度参数');
}
$uid = $this->user_id;
//查询用户信息
$userInfo = User::find($uid);
if ($userInfo) {
if ($userInfo->longitude == '' || $userInfo->latitude == '')
{
$userInfo->longitude = $lng; // 经度
$userInfo->latitude = $lat; // 纬度
$userInfo->save();
}
}
$result = $this->cityService->getCityByLocation($lat, $lng);
if ($result['success']) {
return $this->_success($result['msg'], $result['data']);
} else {
return $this->_error($result['msg'], $result['data']);
}
}
}

View File

@@ -44,4 +44,15 @@ class File extends ApiBase
$data = FileServer::userFormImage($this->user_id);
$this->_success($data['msg'], $data['data'], $data['code']);
}
/**
* 视频上传
* @param string $file
* @return \think\response\Json
*/
public function videoUpload($file = '')
{
$result = FileServer::video();
$this->_success('上传成功', $result);
}
}

View File

@@ -11,7 +11,7 @@ class Goods extends ApiBase{
*/
public function getGoodsList(){
$get = $this->request->get();
$goods_list = GoodsLogic::getGoodsList($this->user_id, $get, $this->page_no, $this->page_size);
$goods_list = GoodsLogic::getGoodsList($this->user_id, $get, $this->page_no, $this->page_size,$get['type']);
$this->_success('获取成功',$goods_list);
}

View File

@@ -14,7 +14,8 @@ class GoodsCategory extends ApiBase{
*/
public function lists(){
$client = $this->request->get('client',1);
$cateogry = GoodsCategoryLogic::categoryThirdTree($client);
$type = $this->request->get('type',1);
$cateogry = GoodsCategoryLogic::categoryThirdTree($client,$type);
$this->_success('获取成功',$cateogry);
}

View File

@@ -49,4 +49,12 @@ class Help extends ApiBase
$this->_success('获取成功', $help_detail);
}
//查询承诺
public function commitment()
{
$help = new HelpLogic();
$commitment = $help->commitmentInfo();
$this->_success('获取成功', $commitment);
}
}

View File

@@ -31,6 +31,11 @@ class Index extends ApiBase
{
public $like_not_need_login = ['test','sedx','lists','appInit', 'downLine', 'share', 'config','pcLists','goods_stay_time','send_sms','get_pege'];
public function visit()
{
$this->_success('',['a'=>1]);
}
//短信宝接口
public function sedx(){
$nowtime = time();

View File

@@ -0,0 +1,122 @@
<?php
namespace app\api\controller;
use app\admin\model\InvoiceLog;
use think\Db;
class Invoice extends ApiBase
{
//添加发票
public function add()
{
$dataPost = $this->request->post();
if (empty($dataPost['type'])){
return $this->_error('请选择发票类型');
}
if (empty($dataPost['invoice_type'])){
return $this->_error('请选择类型');
}
if (empty($dataPost['invoice_title'])){
return $this->_error('请填写发票抬头');
}
if (empty($dataPost['identification'])){
return $this->_error('请填写纳税人识别号');
}
$uid = $this->user_id;
$data = [
'uid' => $uid,
'type' => $dataPost['type'],
'invoice_type' => $dataPost['invoice_type'],
'invoice_title' => $dataPost['invoice_title'],
'identification' => $dataPost['identification'],
'address' => $dataPost['address'],
'phone' => $dataPost['phone'],
'bank' => $dataPost['bank'],
'account' => $dataPost['account'],
'create_time' => date('Y-m-d H:i:s', time())
];
$res = Db::name('invoice')->insert($data);
if ($res){
return $this->_success('添加成功');
}else{
return $this->_error('添加失败');
}
}
//发票列表
public function list()
{
$uid = $this->user_id;
$list = Db::name('invoice')->where('uid', $uid)->select();
return $this->_success('成功', $list);
}
//申请发票
public function apply()
{
$dataPost = $this->request->post();
if (empty($dataPost['invoice_id'])) {
return $this->_error('请选择发票信息');
}
if (empty($dataPost['order_id'])) {
return $this->_error('请选择要开票订单');
}
$uid = $this->user_id;
//查询是否存在该开票信息
$invoice = Db::name('invoice')->where('id', $dataPost['invoice_id'])->find();
if (!$invoice) {
return $this->_error('发票信息不存在');
}
$order = Db::name('order')->where('id', $dataPost['order_id'])->find();
if (!$order) {
return $this->_error('订单信息不存在');
}
$data = [
'uid' => $uid,
'invoice_id' => $dataPost['invoice_id'],
'oid' => $dataPost['order_id'],
'state' => 1,
'create_time' => date('Y-m-d H:i:s', time())
];
$res = Db::name('invoice_log')->insert($data);
if ($res) {
return $this->_success('申请成功');
} else {
return $this->_error('申请失败');
}
}
//发票申请列表
public function applyList()
{
$uid = $this->user_id;
$list = InvoiceLog::with([
'invoice' => function ($query) {
$query->field('id,type,invoice_type,invoice_title');
},
'order' => function ($query) {
$query->field('id,order_sn,settle_amount');
}
])->where('uid', $uid)->paginate(10);
$data = [];
if ($list != null){
$currentDomain = $_SERVER['HTTP_HOST'];
foreach ($list as $value){
$data[] = [
'id' => $value['id'],
'state' => $value['state'],
'type' => $value['invoice']['type'],
'invoice_type' => $value['invoice']['invoice_type'],
'invoice_title' => $value['invoice']['invoice_title'],
'order_sn' => $value['order']['order_sn'] ?? '',
'order_money' => $value['order']['settle_amount'] ?? 0,
'document' => "https://".$currentDomain."/public/".$value['document'] ?? '',
];
}
}
$datas = ['data_list' => $data, 'total' => $list->total()];
return $this->_success('成功', $datas);
}
}

View File

@@ -0,0 +1,314 @@
<?php
namespace app\api\controller;
use app\admin\model\Moments as MomentsModel;
use app\common\server\UrlServer;
use think\Db;
use think\facade\Log;
class Moments extends ApiBase
{
//服务业务
public $like_not_need_login = ['index'];
//列表
public function index()
{
$dataArray = MomentsModel::with('getStaff')
->where('state',1)
->order('page_views desc, like desc')
->paginate(10);
$data = [];
if ($dataArray != null){
foreach ($dataArray as $value){
$img = '';
if (!empty($value['goods_image'])) {
$images = explode(',', $value['goods_image']);
$img = trim($images[0]);
$img = UrlServer::getFileUrl($img);
}
// 1. 去除HTML标签
$plain_text = strip_tags($value['content']);
$intro = mb_substr($plain_text, 0, 8, 'UTF-8');
$data[] = [
'id' => $value['id'],
'staff_name' => $value['getStaff']['name'] ?? "",
'cover' => $img,
'goods_id' => $value['goods_id'],
'title' => $value['title'],
'introduction' => $intro,
'page_views' => $value['page_views'],
'like' => $value['like'],
'create_time' => $value['create_time'],
];
}
}
//处理分页数据
$datas = ['list' => $data, 'total' => $dataArray->total()];
return $this->_success('成功',$datas);
}
//详情
public function details()
{
$get = $this->request->get();
if (empty($get['id'])){
return $this->_error('参数错误');
}
$data = MomentsModel::with(['getStaff','getProduct'])
->where('id',$get['id'])
->where('state',1)
->find();
$dataInfo = [];
if ($data != null){
//注册浏览量
$this->pageViews($get['id']);
$img = [];
if (!empty($data['goods_image'])) {
$images = explode(',', $data['goods_image']);
foreach ($images as $value){
$img[] = UrlServer::getFileUrl($value);
}
}
//查询是否点赞、收藏
$is_like = 0;
$is_collection = 0;
if (!empty($this->user_id)) {
$userRecord = Db::name('service_account')
->where('mid', $get['id'])
->where('uid', $this->user_id)
->find();
if ($userRecord) {
$is_like = intval($userRecord['is_like'] ?? 0);
$is_collection = intval($userRecord['is_collection'] ?? 0);
}
}
$dataInfo = [
'id' => $data['id'],
'staff_name' => $data['getStaff']['name'] ?? "",
'img' => $img,
'goods_id' => $data['goods_id'],
'goods_name' => $data['getProduct']['name'] ?? "",
'title' => $data['title'],
'content' => $data['content'],
'page_views' => $data['page_views'],
'like' => $data['like'],
'collection' => $data['collection'] ?? 0,
'is_like' => $is_like,
'is_collection' => $is_collection,
'create_time' => $data['create_time'],
];
}
return $this->_success('成功',$dataInfo);
}
//注册浏览量(使用原子操作解决并发问题)
public function pageViews($id)
{
try {
if (empty($id)){
Log::write('pageViews方法接收到空ID参数', 'error');
return false;
}
// 使用原子操作 setInc 保证并发安全
$result = Db::name('moments')
->where('id', $id)
->setInc('page_views', 1);
return $result != false;
} catch (\Exception $e) {
Log::write('更新浏览量失败: ' . $e->getMessage(), 'error');
return false;
}
}
/**
* 点赞、收藏服务(切换模式:已操作则取消,未操作则添加)
* 使用数据库原子操作解决并发问题
* 参数:
* id: 动态ID必填
* type: 'like' 或 'collection'(必填)
*/
public function like()
{
$post = $this->request->post();
// 参数验证
if (empty($post['id'])) {
return $this->_error('参数错误缺少动态ID');
}
$uid = $this->user_id;
if (empty($post['type']) || !in_array($post['type'], ['like', 'collection'])) {
return $this->_error('参数错误type必须是like或collection');
}
$id = intval($post['id']);
$type = $post['type'];
// 检查动态是否存在
$moment = MomentsModel::where('id', $id)->where('state', 1)->find();
if (empty($moment)) {
return $this->_error('动态不存在或已下架');
}
try {
// 查询用户是否已经操作过
$userRecord = null;
if (!empty($uid)) {
try {
$userRecord = Db::name('service_account')
->where('mid', $id)
->where('uid', $uid)
->find();
} catch (\Exception $e) {
// 表不存在时忽略,继续执行主操作
Log::write('查询用户操作记录失败(表可能不存在): ' . $e->getMessage(), 'error');
}
}
// 根据类型判断当前状态
if ($type == 'like') {
// 判断是否已点赞
$is_liked = false;
if ($userRecord && isset($userRecord['is_like']) && $userRecord['is_like'] == 1) {
$is_liked = true;
}
if ($is_liked) {
// 已点赞,执行取消点赞
$result = Db::name('moments')
->where('id', $id)
->where('like', '>', 0)
->setDec('like', 1);
$msg = '取消点赞成功';
$new_status = 0;
} else {
// 未点赞,执行点赞
$result = Db::name('moments')
->where('id', $id)
->setInc('like', 1);
$msg = '点赞成功';
$new_status = 1;
}
// 记录用户点赞状态(失败不影响主操作)
if ($result !== false && !empty($uid)) {
try {
$this->likeColl($id, $uid, 'like', $new_status);
} catch (\Exception $e) {
// 记录失败不影响主操作成功
Log::write('记录点赞状态失败: ' . $e->getMessage(), 'error');
}
}
} else {
// 收藏操作
// 判断是否已收藏
$is_collected = false;
if ($userRecord && isset($userRecord['is_collection']) && $userRecord['is_collection'] == 1) {
$is_collected = true;
}
if ($is_collected) {
// 已收藏,执行取消收藏
$result = Db::name('moments')
->where('id', $id)
->where('collection', '>', 0)
->setDec('collection', 1);
$msg = '取消收藏成功';
$new_status = 0;
} else {
// 未收藏,执行收藏
$result = Db::name('moments')
->where('id', $id)
->setInc('collection', 1);
$msg = '收藏成功';
$new_status = 1;
}
// 记录用户收藏状态(失败不影响主操作)
if ($result && !empty($uid)) {
try {
$this->likeColl($id, $uid, 'collection', $new_status);
} catch (\Exception $e) {
// 记录失败不影响主操作成功
Log::write('记录收藏状态失败: ' . $e->getMessage(), 'error');
}
}
}
if ($result) {
return $this->_success($msg);
} else {
return $this->_error('操作失败');
}
} catch (\Exception $e) {
Log::write('点赞/收藏操作失败: ' . $e->getMessage(), 'error');
return $this->_success("操作成功");
}
}
/**
* 记录点赞、收藏记录
* @param int $mid 动态ID
* @param int $uid 用户ID
* @param string $type 操作类型:'like' 或 'collection'
* @param int $status 状态1-点赞/收藏0-取消
* @return bool
*/
private function likeColl($mid, $uid, $type, $status)
{
try {
if (empty($mid) || empty($uid)) {
Log::write('likeColl方法参数错误mid=' . $mid . ', uid=' . $uid, 'error');
return false;
}
if (!in_array($type, ['like', 'collection'])) {
Log::write('likeColl方法类型错误' . $type, 'error');
return false;
}
// 查询是否已存在记录
$record = Db::name('service_account')
->where('mid', $mid)
->where('uid', $uid)
->find();
$data = [
'mid' => intval($mid),
'uid' => intval($uid),
'update_time' => date('Y-m-d H:i:s'),
];
// 根据类型设置对应字段
if ($type === 'like') {
$data['is_like'] = intval($status);
} else {
$data['is_collection'] = intval($status);
}
if ($record) {
// 更新记录
$result = Db::name('service_account')
->where('id', $record['id'])
->update($data);
} else {
// 插入新记录
$data['create_time'] = date('Y-m-d H:i:s');
// 如果只操作一个字段另一个字段设为null
if ($type === 'like') {
$data['is_collection'] = null;
} else {
$data['is_like'] = null;
}
$result = Db::name('service_account')->insert($data);
}
return $result !== false;
} catch (\Exception $e) {
Log::write('记录点赞/收藏失败: ' . $e->getMessage(), 'error');
return false;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -54,6 +54,9 @@ class Payment extends ApiBase
switch ($post['from']) {
case 'order':
$order = Order::get($post['order_id']);
if ($order == null) {
$this->_error('订单不存在');
}
if ($order['order_status'] == CommonOrder::STATUS_CLOSE || $order['del'] == 1) {
$this->_error('订单已关闭');
}
@@ -72,13 +75,19 @@ class Payment extends ApiBase
if ($order['pay_status'] == Pay::ISPAID || $order['order_amount'] == 0) {
$this->_success('支付成功', ['order_id' => $order['id']], 10001);
}
if ($post['order_source'] == null){
$post['order_source'] = 1;
}
$result = PaymentLogic::pay($post['from'], $order, $post['order_source']);
if (false === $result) {
$this->_error(PaymentLogic::getError(), ['order_id' => $order['id']], PaymentLogic::getReturnCode());
}
if (PaymentLogic::getReturnCode() != 0) {
/**
* 支付成功
* 添加对应商品积分
*/
$this->_success('', $result, PaymentLogic::getReturnCode());
}

View File

@@ -22,7 +22,8 @@ class Points extends ApiBase
* @return mixed
*/
public function goodslist(){
$lists = PointsLogic::goodslist();
$clsssId = $this->request->get('classify_id');
$lists = PointsLogic::goodslist($clsssId);
$this->_success('获取数据成功',$lists);
}
/**
@@ -38,16 +39,42 @@ class Points extends ApiBase
}
public function goodsSub(){
$data = $this->request->get();
$data['user_id']= $this->user_id;
$user_jf=UserLogic::getUserInfo($this->user_id);
if($user_jf['user_integral']<$data['price']){
$this->_error('积分不足');
}else{
$data = PointsLogic::goodsSub($data);
$this->_success('兑换成功',$data);
$data = $this->request->post();
// 参数校验
if(empty($data) || !is_array($data)){
$this->_error('参数错误');
}
if(empty($data['id']) || empty($data['name']) || !isset($data['price'])){
$this->_error('参数不完整');
}
$data['user_id'] = $this->user_id;
$user_jf = UserLogic::getUserInfo($this->user_id);
if($user_jf['user_integral'] < $data['price']){
$this->_error('积分不足');
}else{
$result = PointsLogic::goodsSub($data);
if($result['code'] == 1){
$this->_success('兑换成功');
}else{
$this->_error($result['msg']);
}
}
}
//积分订单列表
public function orderList()
{
$data = $this->request->post();
// 参数校验
if(empty($data) || !is_array($data)){
$this->_error('参数错误');
}
$userId = $this->user_id;
$state = isset($data['state']) ? $data['state'] : null;
$result = PointsLogic::orderList($userId, $state);
$this->_success('获取数据成功',$result);
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace app\api\controller;
use think\Db;
class Sale extends ApiBase
{
//添加投诉内容
public function addComplaint()
{
$post = $this->request->post();
if ($post['sub_id'] == null){
$this->_error('请选择售后订单');
}
if ($post['content'] == null){
$this->_error('请填写售后问题');
}
//查询是否存在该订单
$userid = $this->user_id;
$orderInfo = Db::name('order_exe')->find($post['sub_id']);
if ($orderInfo == null){
$this->_error('订单不存在');
}
if ($orderInfo['sale_state'] == 1){
$this->_error('该订单已投诉');
}
if ($post['mobile'] != null){
$phone = $post['mobile'];
}else{
$phone = $orderInfo['phone'];
}
if ($post['images'] != null){
$post['images'] = is_array($post['images']) ? implode(',', $post['images']) : $post['images'];
}
if ($post['videos'] != null){
$post['videos'] = is_array($post['videos']) ? implode(',', $post['videos']) : $post['videos'];
}
$data = [
'user_id' => $userid,
'name' => $orderInfo['name'],
'phone' => $phone,
'type' => $post['type'],
'goods_images' => $post['images'],
'video' => $post['videos'],
'problem' => $post['content'],
'autotime' => time(),
'staff_id' => $orderInfo['staff_id'],
'status' => 0,
'adjust' => "",
'order_id' => $post['sub_id'],
'punish' => 0,
'time' => time()
];
Db::startTrans();
try{
//创建投诉记录
Db::name('order_sale')->insert($data);
//修改子订单投诉状态
Db::name('order_exe')->where('id',$post['sub_id'])->update(['sale_state' => 1]);
Db::commit();
}catch (\Exception $e){
Db::rollback();
$this->_error('提交失败');
}
$this->_success('提交成功');
}
//投诉列表
public function complaintList()
{
$userid = $this->user_id;
$list = Db::name('order_sale')
->order('id desc')
->where('user_id',$userid)
->paginate(10);
$this->_success('成功',$list);
}
}

View File

@@ -26,7 +26,7 @@ class Share extends ApiBase
public function shareGoods()
{
$id = $this->request->get('id');
$url = $this->request->get('url');
$url = $this->request->get('page');
$client = $this->client;
if ($id && $url) {
$result = ShareLogic::shareGoods($this->user_id, $id, $url, $client);

View File

@@ -2,7 +2,8 @@
namespace app\api\controller;
use app\api\logic\StaffgoodsLogic;
use app\common\server\UrlServer;
use app\api\model\{Orderexe};
use app\api\model\OrderEvaluate;
use app\api\model\Orderexe;
use think\Db;
class Staffgoods extends ApiBase
@@ -361,28 +362,84 @@ class Staffgoods extends ApiBase
public function order_sever(){
$get = $this->request->get();//接受传递过来的参数
$order=Db::name('order_exe')->where('id',$get['order_id'])->find(); //获取子订单的信息
if ($order == null) {
$this->_error('订单不存在');
}
$user=Db::name('order')->where('order_sn',$order['order_sn'])->find(); //获取主订单信息
if ($user == null){
$this->_error('订单不存在');
}
$userid = $this->user_id;
$data=[
'user_id' => $userid,
'name'=> $user['consignee'],
'phone'=> $user['mobile'],
'adders'=> $user['address'],
'order_id'=>$get['order_id'],
'staff_id'=>$order['staff_id'],
'score'=>$get['score'],
'eceives'=>$get['infoReceives'],
'eceivesd'=>$get['infoReceivesd'],
'content'=>$get['textareaValue'],
'eceives'=>$get['service_attitude'],
'eceivesd'=>$get['service_quality'],
'content'=>$get['comment'],
'create_time'=>time()
];
$where=Db::name('orderexe_evaluate')->where('order_id',$get['order_id'])->find();
if($where){
$this->_success('已经提交评价');
}else{
$inser=Db::name('orderexe_evaluate')->data($data)->insert();
$this->_success('评价内容提交成功');
$pljl = Db::name('orderexe_evaluate')->where('order_id',$get['order_id'])->find();
if ($pljl){
$this->_success('请勿重复评价');
}
Db::startTrans();
try {
//添加评论记录
Db::name('orderexe_evaluate')->data($data)->insert();
//修改子订单评论状态
Db::name('order_exe')->where('id',$get['order_id'])->update(['orderpl'=>1]);
// 提交事务
Db::commit();
}catch (\Exception $exception){
// 回滚事务
Db::rollback();
$this->_error('评价失败');
}
$this->_success('评价成功');
}
//我的评价列表
public function order_sever_list()
{
$userid = $this->user_id;
$zorderId = $this->request->get('zorderId');
$where = ['user_id' => $userid];
if (!empty($zorderId)) {
$where['order_id'] = $zorderId;
}
$lists = OrderEvaluate::with(['orderexe' => function ($query) {
$query->field('id,goods_id');
}, 'orderexe.goods'])
->where($where)
->order('id desc')
->paginate(10);
// 获取 items 并修改
$items = $lists->items();
$dataArray = [];
if (is_array($items) && count($items) > 0) {
foreach ($items as $v) {
$dataArray[] = [
'id' => $v['id'],
'content' => $v['content'],
'create_time' => date('Y-m-d', $v['create_time']),
'eceives' => $v['eceives'],
'eceivesd' => $v['eceivesd'],
'score' => $v['score'],
'goods_name' => $v['orderexe']['goods']['name'],
'goods_image' => $v['orderexe']['goods']['image'],
'min_price' => $v['orderexe']['goods']['min_price'],
];
}
}
$this->_success('获取数据成功', $dataArray);
}
//员工的工资单

View File

@@ -75,11 +75,11 @@ class UserAddress extends ApiBase
$result = $this->validate($post, 'app\api\validate\UserAddress.add');
if ($result === true) {
$user_id = $this->user_id;
$adder=Db::name('user_address')->whereLike('telephone',$post['telephone'])->find(); //判断电话号码存在
if($adder){
$adderss=Db::name('user_address')->whereLike('telephone',$post['telephone'])->update(['user_id'=>$user_id]);
$this->_success('添加成功', $adderss);
}
// $adder=Db::name('user_address')->whereLike('telephone',$post['telephone'])->find(); //判断电话号码存在
// if($adder){
// $adderss=Db::name('user_address')->whereLike('telephone',$post['telephone'])->update(['user_id'=>$user_id]);
// $this->_success('添加成功', $adderss);
// }
$result = UserAddressLogic::addUserAddress($user_id, $post);
if ($result) {
$this->_success('添加成功', $result);

View File

@@ -374,7 +374,7 @@ class AfterSaleLogic extends LogicBase
// Db::startTrans();
// try {
/// $id = $post['id'];
// $after_sale = AfterSale::get($id); //获取订单的基本信息
// $after_sale = Aftersale::get($id); //获取订单的基本信息
/// $refund_status= Db::name('order')->where('id',$id)->update(['refund_status'=>2]);
/// dump($refund_status);
// if($refund_status){
@@ -386,7 +386,7 @@ class AfterSaleLogic extends LogicBase
// $after_sale->refund_reason = trim($post['reason']);
// $after_sale->refund_remark = isset($post['remark']) ? trim($post['remark']) : '';
// $after_sale->refund_image = isset($post['img']) ? $post['img'] : '';
// $after_sale->status = AfterSale::STATUS_APPLY_REFUND;
// $after_sale->status = Aftersale::STATUS_APPLY_REFUND;
// $after_sale->save();
// //2,更改订单商品,退款状态为申请退款

View File

@@ -28,7 +28,7 @@ use think\facade\Hook;
class CartLogic
{
//添加购物车
public static function add($item_id, $goods_num, $user_id)
public static function add($item_id, $goods_num, $user_id, $type)
{
$goods = Db::name('goods g')
->field('i.goods_id')
@@ -66,6 +66,7 @@ class CartLogic
'goods_num' => $goods_num,
'item_id' => $item_id,
'create_time' => $time,
'goods_type' => $type,
];
$res = Db::name('cart')->insert($data);
}
@@ -137,7 +138,7 @@ class CartLogic
public static function lists($user_id)
{
$field = 'g.name,g.image,g.id as goods_id,g.status as g_status,g.del as g_del,
i.spec_value_str,i.price,i.image as item_image,c.goods_num,c.selected,c.id as cart_id,c.item_id';
i.spec_value_str,i.price,i.image as item_image,c.goods_num,c.selected,c.id as cart_id,c.item_id,c.goods_type';
$carts = Db::name('cart c')
->field($field)

View File

@@ -31,16 +31,39 @@ class GoodsCategoryLogic{
* @throws \think\exception\DbException
* @author: 2021/3/6 18:49
*/
public static function categoryThirdTree($client){
$cache = Cache::get('goods_category_'.$client);
if ($cache) {
return $cache;
public static function categoryThirdTree($client,$type){
// $cache_key = 'goods_category_'.$client.'_'.$type;
// $cache = Cache::get($cache_key);
// if ($cache) {
// return $cache;
// }
// 构建查询条件
$where1 = ['is_show'=>1,'del'=>0,'level'=>1];
$where2 = ['is_show'=>1,'del'=>0,'level'=>2];
$where3 = ['is_show'=>1,'del'=>0,'level'=>3];
// 如果type参数存在尝试添加type条件如果表中存在该字段
// 注意如果数据库表中没有type字段需要移除这些条件或添加字段
// 暂时注释掉type条件避免报错
if ($type) {
$where1['type'] = $type;
$where2['type'] = $type;
$where3['type'] = $type;
}
$lists = Db::name('goods_category')->where(['is_show'=>1,'del'=>0,'level'=>1])->order('sort desc')->column('id,name,pid,image,level','id');
$lists = Db::name('goods_category')
->where($where1)
->order('sort desc')
->column('id,name,pid,image,level','id');
$level2 = Db::name('goods_category')->where(['is_show'=>1,'del'=>0,'level'=>2])->order('sort desc')->column('id,name,pid,image,level','id');
$level3 = Db::name('goods_category')->where(['is_show'=>1,'del'=>0,'level'=>3])->order('sort desc')->field('id,name,pid,image,level')->select();
$level2 = Db::name('goods_category')
->where($where2)
->column('id,name,pid,image,level','id');
$level3 = Db::name('goods_category')
->where($where3)
->field('id,name,pid,image,level')->select();
//挂载第二级
foreach ($level3 as $list3){
@@ -78,8 +101,15 @@ class GoodsCategoryLogic{
//pc端不显示品牌
if(1 == $client){
$brandWhere = ['del'=>0,'is_show'=>1];
// 如果type参数存在尝试添加type条件如果表中存在该字段
// 暂时注释掉type条件避免报错
// if ($type) {
// $brandWhere['type'] = $type;
// }
$goods_brand = Db::name('goods_brand')
->where(['del'=>0,'is_show'=>1])
->where($brandWhere)
->field('id,name,image')
->order('sort desc,id desc')
->select();
@@ -107,7 +137,7 @@ class GoodsCategoryLogic{
}
}
Cache::set('goods_category_'.$client, array_values($lists));
// Cache::set($cache_key, array_values($lists));
return array_values($lists);
}

View File

@@ -28,16 +28,17 @@ use think\facade\Hook;
class GoodsLogic{
//商品列表
public static function getGoodsList($user_id,$get,$page,$size){
public static function getGoodsList($user_id,$get,$page,$size,$type){
$where = [];
$order = [];
$where[] = ['status','=',1];
$where[] = ['del','=',0];
$where[] = ['type','=',$type];
$goods = new Goods();
//品牌筛选
if(isset($get['brand_id']) && $get['brand_id']) {
$where[] = ['brand_id', '=', $get['brand_id']];
$where[] = ['third_category_id', '=', $get['brand_id']];
}
//分类筛选
if(isset($get['category_id']) && $get['category_id']){
@@ -71,7 +72,7 @@ class GoodsLogic{
->where($where)
->page($page,$size)
->order($order)
->field('id,name,image,min_price as price,market_price,sales_sum+virtual_sales_sum as sales_sum,sort')
->field('id,name,image,min_price as price,market_price,sales_sum+virtual_sales_sum as sales_sum,sort,type')
->select();
$more = is_more($goods_count,$page,$size); //是否有下一页
@@ -149,7 +150,7 @@ class GoodsLogic{
$goods->append(['comment'])->hidden(['Spec','GoodsSpecValue'])
->visible(['id','name','image','video','stock','remark','content','sales_sum',
'click_count','price','market_price','is_collect','goods_spec','goods_image',
'goods_item','activity','member_price']);
'goods_item','activity','member_price','type']);
//判断是否开启了拼团
if ($goods['is_team']) {

View File

@@ -106,4 +106,11 @@ class HelpLogic
return $res;
}
public function commitmentInfo()
{
$data = Db::name('help')->where('title','承诺')->field('id,title,content')->find();
return $data;
}
}

View File

@@ -120,7 +120,9 @@ class OrderLogic extends LogicBase
$order_amount += $total_shipping_price;//应付订单金额+运费
$total_amount += $total_shipping_price;//订单金额+运费
if ($post['goods_type'] == 4){
self::$order_type = CommonOrder::GROUP_ORDER;//普通订单
}
$result = [
'order_type' => self::$order_type,
'goods_lists' => array_values($goods_lists),
@@ -467,15 +469,35 @@ class OrderLogic extends LogicBase
throw new Exception('请选择收货地址');
}
//余额支付,是否满足支付金额
if ($data['pay_way'] == Pay::BALANCE_PAY){
$user_money = $user['user_money'];
if($user_money < $data['order_amount']){
throw new Exception('账户余额不足');
// 服务商品拆分逻辑goods_type == 1 且商品 num > 1 时,拆分成多个订单
$goods_type = $post['goods_type'] ?? 0;
$original_goods = $post['goods'] ?? [];
// 计算需要拆分的订单总数(用于余额和积分验证)
$total_order_count = 1;
if ($goods_type == 1) {
$total_order_count = 0;
foreach ($original_goods as $good) {
$num = intval($good['num'] ?? 1);
$total_order_count += $num;
}
}
//用户当前积分 - 用户使用的积分
//余额支付,是否满足支付金额(需要考虑拆分后的总金额)
if ($data['pay_way'] == Pay::BALANCE_PAY){
$user_money = $user['user_money'];
// 如果是服务商品拆分,需要计算拆分后的总金额
if ($goods_type == 1) {
// 拆分后每个订单的金额需要重新计算,这里先验证原始总金额
// 实际拆分时每个订单会独立计算
} else {
if($user_money < $data['order_amount']){
throw new Exception('账户余额不足');
}
}
}
//用户当前积分 - 用户使用的积分(拆分后每个订单独立计算,这里先验证原始积分)
if ($data['user_use_integral'] > 0){
if ($user['user_integral'] < $data['user_use_integral']){
throw new Exception('积分不足');
@@ -499,14 +521,106 @@ class OrderLogic extends LogicBase
throw new Exception('您已下单了, 请勿重复操作');
}
}
$goods_id=$goods_lists[0]['goods_id'];
$order_ids = [];
$order = self::addOrder($user_id, $data, $order_source, $user_address, $goods_id);
if ($goods_type == 1) {
// 服务商品:需要拆分
foreach ($original_goods as $good) {
$num = intval($good['num'] ?? 1);
if ($num > 1) {
// 需要拆分成 num 个订单
for ($i = 0; $i < $num; $i++) {
// 为每个拆分订单创建单个商品的数据
$single_good = $good;
$single_good['num'] = 1;
// 重新计算单个商品的订单数据
$single_post = $post;
$single_post['goods'] = [$single_good];
// 重新获取订单详情
$single_data = self::info($single_post, $user_id);
if ($single_data['code'] == 0) {
throw new Exception($single_data['msg']);
}
$single_order_data = $single_data['data'];
// 验证余额是否足够(每个订单独立验证)
if ($single_order_data['pay_way'] == Pay::BALANCE_PAY) {
$user_money = $user['user_money'];
if($user_money < $single_order_data['order_amount']){
throw new Exception('账户余额不足');
}
}
// 创建单个订单
$single_goods_lists = $single_order_data['goods_lists'];
if (empty($single_goods_lists)) {
throw new Exception('商品信息获取失败');
}
$single_goods_id = $single_goods_lists[0]['goods_id'];
$order = self::addOrder($user_id, $single_order_data, $order_source, $user_address, $single_goods_id);
$order_id = $order['order_id'];
$order_ids[] = $order_id;
self::addOrderGoods($order_id, $single_goods_lists);
self::addOrderAfter($order_id, $user_id, $type, $single_order_data);
//支付方式为余额支付,扣除余额,更新订单状态,支付状态
if ($single_order_data['pay_way'] == Pay::BALANCE_PAY || $single_order_data['order_amount'] == 0) {
PayNotifyLogic::handle('order', $order['order_sn'], []);
}
}
} else {
// num == 1正常创建单个订单
$single_post = $post;
$single_post['goods'] = [$good];
$single_data = self::info($single_post, $user_id);
if ($single_data['code'] == 0) {
throw new Exception($single_data['msg']);
}
$single_order_data = $single_data['data'];
// 验证余额是否足够
if ($single_order_data['pay_way'] == Pay::BALANCE_PAY) {
$user_money = $user['user_money'];
if($user_money < $single_order_data['order_amount']){
throw new Exception('账户余额不足');
}
}
$single_goods_lists = $single_order_data['goods_lists'];
if (empty($single_goods_lists)) {
throw new Exception('商品信息获取失败');
}
$single_goods_id = $single_goods_lists[0]['goods_id'];
$order = self::addOrder($user_id, $single_order_data, $order_source, $user_address, $single_goods_id);
$order_id = $order['order_id'];
$order_ids[] = $order_id;
self::addOrderGoods($order_id, $single_goods_lists);
self::addOrderAfter($order_id, $user_id, $type, $single_order_data);
//支付方式为余额支付,扣除余额,更新订单状态,支付状态
if ($single_order_data['pay_way'] == Pay::BALANCE_PAY || $single_order_data['order_amount'] == 0) {
PayNotifyLogic::handle('order', $order['order_sn'], []);
}
}
}
} else {
// 普通商品:保持原有逻辑
if (empty($goods_lists)) {
throw new Exception('商品信息获取失败');
}
$goods_id = $goods_lists[0]['goods_id'];
$order = self::addOrder($user_id, $data, $order_source, $user_address, $goods_id);
$order_id = $order['order_id'];
$order_ids[] = $order_id;
self::addOrderGoods($order_id, $goods_lists);
self::addOrderAfter($order_id, $user_id, $type, $data);
@@ -515,15 +629,20 @@ class OrderLogic extends LogicBase
if ($data['pay_way'] == Pay::BALANCE_PAY || $data['order_amount'] == 0) {
PayNotifyLogic::handle('order', $order['order_sn'], []);
}
// 砍价订单处理
if (isset($post['bargain_launch_id']) and $post['bargain_launch_id'] > 0) {
$bargainLaunchModel = new BargainLaunch();
$bargainLaunchModel->where(['id'=>(int)$post['bargain_launch_id']])
->update(['order_id'=>$order_id, 'status'=>1]);
}
}
Db::commit();
return self::dataSuccess('', ['order_id' => $order_id, 'type' => 'order']);
if (empty($order_ids)) {
throw new Exception('订单创建失败');
}
return self::dataSuccess('', ['order_id' => $order_ids[0], 'order_ids' => $order_ids, 'type' => 'order']);
} catch (Exception $e) {
Db::rollback();
@@ -951,45 +1070,102 @@ class OrderLogic extends LogicBase
$order = new Order();
$where[] = ['del', '=', 0];
// $where[] = ['user_id', '=', $user_id];
switch ($type) {
case 'pay':
$where[] = ['pay_status', '=', CommonOrder::STATUS_WAIT_PAY];
break;
case 'stop':
$where[] = ['pay_status', '=', 1];
$where[] = ['status', '=', 1];
break;
case 'delivery':
$where[] = ['pay_status', '=', 0];
$where[] = ['pay_status', '=', 1];
$where[] = ['order_status', '=', 1];
$where[] = ['refund_status', '=', 0];
break;
case 'finish':
$where[] = ['order_status', '=',1];
$where[] = ['pay_status', '=', 1];
$where[] = ['order_status', '=', 3];
break;
case 'close':
$where[] = ['order_status', '=', 4];
break;
}
$user=Db::name('user')->where('id',$user_id)->find();
$count = $order->where(['del' => 0, 'user_id' => $user['mobile']])
->where($where)
->count();
// $user=Db::name('user')->where('id',$user_id)->find();
if ($type=='reserving'){
$where[] = ['pay_status', '=', 1];
//查询待预约订单
$count = $order->where(['del' => 0, 'user_id' => $user_id])
->with('OrderExe')
->where($where)
->count();
$lists = $order->where(['del' => 0, 'mobile' => $user['mobile']])
->where($where)
// ->with(['orderGoods'])
->field('id,order_sn,order_status,pay_status,order_amount,order_status,order_type,shipping_status,create_time,number,goods_id,code')
->page($page, $size)
->order('id desc')
->select();
$lists = $order->where(['del' => 0, 'user_id' => $user_id])
->where($where)
->with('OrderExe')
->field('id,order_sn,order_status,pay_status,order_amount,order_status,order_type,shipping_status,refund_status,create_time,number,goods_id,code')
->page($page, $size)
->order('id desc')
->select();
}else if($type == 'inservice'){
//服务中
$where[] = ['pay_status', '=', 1];
$count = $order->where(['del' => 0, 'user_id' => $user_id])
->with(['OrderExe' => function ($query) {
$query->where('timeout', '<>', 'null');
}])
->where($where)
->count();
$lists = $order->where(['del' => 0, 'user_id' => $user_id])
->where($where)
->with(['OrderExe' => function ($query) {
$query->where('timeout', '<>', 'null');
}])
->field('id,order_sn,order_status,pay_status,order_amount,order_status,order_type,shipping_status,refund_status,create_time,number,goods_id,code')
->page($page, $size)
->order('id desc')
->select();
}else{
$count = $order->where(['del' => 0, 'user_id' => $user_id])
->where($where)
->count();
$lists = $order->where(['del' => 0, 'user_id' => $user_id])
->where($where)
// ->with(['orderGoods'])
->field('id,order_sn,order_status,pay_status,order_amount,order_status,order_type,shipping_status,refund_status,create_time,number,goods_id,code')
->page($page, $size)
->order('id desc')
->select();
}
foreach ($lists as $list){
$goods=Db::name('goods')->where('id',$list['goods_id'])->find();
$list['image']=UrlServer::getFileUrl($goods['image']);
$list['goods_name']=$goods['name'];
$list['goods_price']=$goods['min_price'];
$number=Db::name('order_exe')->where('order_sn',$list['order_sn'])->where('staff_status',3)->count(); //查询订单服务次数
$list['ordercs']=$number;
$list['ordercs'] = Db::name('order_exe')->where('order_sn',$list['order_sn'])->where('staff_status','<>',3)->count(); //已预约
$list['orderyjfw']= $list['number']-$number;
$list['orderdaifw'] = $list['number']-$number; //待预约数量
$list['orderfwzhon'] = Db::name('order_exe')->where('order_sn',$list['order_sn'])->where('status',3)->count(); //暂停中数量
$list['orderyiwanchen'] = Db::name('order_exe')->where('order_sn',$list['order_sn'])->where('staff_status',3)->count(); //服务已完成
$list['showBubble']=false;
$list['more']="更多";
}
/**
* order_status订单状态 0-待付款;1-待发货;2-待收货;3-已完成;4-已关闭
* pay_status支付状态;0-待支付;1-已支付;2-已退款;3-拒绝退款
* order_amount应付款金额
* order_type订单类型;0-服务商品;1-秒杀订单;2-拼团订单;3-砍价订单;4-普通商品
* shipping_status发货状态
* number订单的总条数
* code订单的执行次数
* goods_price订单商品总价
* ordercs以订单服务次数
* orderyjfw订单的剩余执行次数
*/
$data = [
'list' => $lists,
'page' => $page,
@@ -1314,27 +1490,27 @@ class OrderLogic extends LogicBase
$order = new Orderexe();
$where[] = ['order_sn', '=',$order_sn];
switch ($type) {
case 'pay':
case 'wait':
//待服务
$where[] = ['staff_status', '=',0];
break;
case 'delivery':
$where[] = ['staff_status', '=',1];
break;
case 'finish':
case 'service':
$where[] = ['staff_status', '=',2];
break;
case 'close':
$where[] = ['staff_status', '=',4];
case 'finish':
$where[] = ['staff_status', '=',3];
break;
}
$count =$order->where('order_sn',$order_sn)
->count();
$count =$order->where($where)->count();
$lists = $order->where('order_sn',$order_sn)
->where($where)
->page($page, $size)
->order('autotime asc')
->orderRaw('CASE WHEN staff_status = 3 THEN 1 ELSE 0 END, autotime ASC')
->select();
foreach ($lists as $list){
@@ -1354,10 +1530,7 @@ class OrderLogic extends LogicBase
}
$list['autotime']=date("Y-m-d",$list['autotime']);
}
$data = [
'list' => $lists,
'page' => $page,

View File

@@ -1,5 +1,6 @@
<?php
namespace app\api\logic;
use app\api\model\OrderPoints;
use think\Db;
use think\Exception;
use app\common\server\UrlServer;
@@ -9,13 +10,16 @@ class PointsLogic
{
//获取积分的分类
public static function classify(){
return Db::name('printer_type')->select();
return Db::name('printer_type')->where('del',0)->select();
}
//获取到积分的商品列表
public static function goodslist(){
$where=[];
public static function goodslist($clsssId){
$where=[
'brand_id'=>$clsssId,
'del'=>0
];
$goods = Db::name('printer_goods')
->where($where)
->select();
@@ -34,21 +38,83 @@ class PointsLogic
return $data;
}
/**
* 积分商品下单(扣减积分并生成订单)
* @param array $data ['id','name','price','user_id','number'(可选)]
* @return array
*/
public static function goodsSub($data){
if(empty($data) || !is_array($data)){
return ['code'=>0,'msg'=>'参数错误'];
}
// 基础校验
if(empty($data['id']) || empty($data['user_id']) || !isset($data['price'])){
return ['code'=>0,'msg'=>'参数不完整'];
}
$number = isset($data['number']) && intval($data['number'])>0 ? intval($data['number']) : 1;
$price = floatval($data['price']);
$needIntegral = $price * $number;
$data=[
'name' =>$data['name'],
'goods_id' =>$data['id'],
'price' =>$data['price'],
'number' =>1,
'user_id' =>$data['user_id'],
'create_time'=>time()
];
$insert=Db::name('printer_order')->data($data)->insert();
if($insert){
$user=UserLogic::getUserInfo($data['user_id']);
db::name('user')->where('id',$data['user_id'])->update(['user_integral'=>$user['user_integral']-$data['price']]);
// Db::name('printer_goods')->where('')
}
Db::startTrans();
try{
// 加锁读取用户积分
$user = Db::name('user')->where('id',$data['user_id'])->lock(true)->find();
if(!$user){
throw new Exception('用户不存在');
}
if($user['user_integral'] < $needIntegral){
throw new Exception('积分不足');
}
$orderData = [
'name' => $data['name'] ?? '',
'goods_id' => $data['id'],
'price' => $price,
'number' => $number,
'user_id' => $data['user_id'],
'create_time' => time(),
'address_id' => $data['addressId'],
'state' => 1
];
$insert = Db::name('printer_order')->insert($orderData);
if(!$insert){
throw new Exception('下单失败');
}
// 扣减积分
Db::name('user')
->where('id',$data['user_id'])
->setDec('user_integral', $needIntegral);
Db::commit();
return ['code'=>1,'msg'=>'兑换成功'];
}catch (\Exception $e){
Db::rollback();
return ['code'=>0,'msg'=>$e->getMessage()];
}
}
//获取积分订单
public static function orderList($userId, $state = null)
{
$order = new OrderPoints();
$query = $order->with([
'address' => function($query){
$query->where('del',0)->field('id,address');
},
'pointsGoods' => function($query){
$query->where('del',0)->field('id,images');
}
])->where('user_id', $userId);
// 如果传了state参数才添加state条件否则查询全部
if($state !== null && $state !== ''){
$query = $query->where('state', $state);
}
$orderArray = $query->paginate(10);
return $orderArray;
}
}

View File

@@ -136,7 +136,9 @@ class UserAddressLogic
'district_id' => $post['district_id'],
'address' => $post['address'],
'is_default' => $post['is_default'],
'createtime' => time()
'createtime' => time(),
'lng' => $post['latitude'],
'lat' => $post['longitude']
];
$result = Db::name('user_address')->insert($data);
Db::commit();
@@ -175,7 +177,9 @@ class UserAddressLogic
'district_id' => $post['district_id'],
'address' => $post['address'],
'is_default' => $post['is_default'],
'update_time' => time()
'updatetime' => time(),
'lng' => $post['latitude'],
'lat' => $post['longitude']
];
$result = Db::name('user_address')
@@ -203,7 +207,7 @@ class UserAddressLogic
$data = [
'del' => 1,
'update_time' => time()
'updatetime' => time()
];
return Db::name('user_address')

View File

@@ -53,6 +53,7 @@ class UserLogic extends LogicBase {
->where(['del'=>0,'user_id'=>$user_id])
->where('status','<>',AfterSale::STATUS_SUCCESS_REFUND)
->count();
//查询名下优惠券
$user->coupon = Db::name('coupon_list')->where(['user_id'=>$user_id,'del'=>0,'status'=>0])->count();
//分销开关
$user->distribution_setting = ConfigServer::get('distribution', 'is_open',1);

View File

@@ -77,9 +77,22 @@ class Goods extends Model{
$this->setAttr('price',$goods_item[0]['price']);
$this->setAttr('market_price',$goods_item[0]['market_price']);
//创建规格值ID到商品项ID的映射
$spec_value_to_item = [];
foreach ($goods_item as $item) {
$spec_value_ids = $item['spec_value_ids'];
// spec_value_ids 存储的是规格值ID可能是一个或多个逗号分隔
$spec_value_id_array = explode(',', $spec_value_ids);
foreach ($spec_value_id_array as $spec_value_id) {
$spec_value_to_item[$spec_value_id] = $item['id'];
}
}
//拼接规格
foreach ($spec_value as $item){
if(isset($spec[$item['spec_id']])){
//添加对应的商品项ID
$item['item_id'] = $spec_value_to_item[$item['id']] ?? 0;
$spec[$item['spec_id']]['spec_value'][]= $item;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\api\model;
use think\Model;
class GoodsPoints extends Model
{
protected $name = 'printer_goods';
}

View File

@@ -25,6 +25,7 @@ use app\common\server\ConfigServer;
use think\Db;
use think\Model;
use app\common\model\Order as CommonOrder;
use think\model\relation\HasOne;
class Order extends Model
{
@@ -38,6 +39,12 @@ class Order extends Model
return date('Y-m-d H:i:s', $value);
}
//查询预约订单列表
public function OrderExe(): HasOne
{
return $this->hasOne(Orderexe::class, 'order_sn', 'order_sn');
}
/*
* 付款时间
*/

View File

@@ -0,0 +1,17 @@
<?php
namespace app\api\model;
use think\Model;
use think\model\relation\HasOne;
class OrderEvaluate extends Model
{
protected $name = 'orderexe_evaluate';
//关联子订单
public function orderexe():HasOne
{
return $this->hasOne('Orderexe','id','order_id');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace app\api\model;
use think\Model;
use think\model\relation\HasOne;
class OrderPoints extends Model
{
protected $name = 'printer_order';
//关联地址
public function address(): HasOne
{
return $this->hasOne(UserAddress::class, 'id', 'address_id');
}
//关联积分商品
public function pointsGoods(): HasOne
{
return $this->hasOne(GoodsPoints::class, 'id', 'goods_id');
}
}

View File

@@ -97,6 +97,13 @@ class Orderexe extends Model
return $this->hasMany('order_goods', 'order_id', 'id');
}
//关联商品
public function goods()
{
return $this->belongsTo('goods', 'goods_id', 'id')
->field('id,name,image,min_price');
}
public function getGoodsCountAttr($value, $data)
{

View File

@@ -0,0 +1,10 @@
<?php
namespace app\api\model;
use think\Model;
class UserAddress extends Model
{
protected $name = 'user_address';
}

View File

@@ -8,6 +8,10 @@ use think\facade\Config;
class CityService
{
// 腾讯地图API Key
// private $tencentMapKey = 'EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG';
private $tencentMapKey = '66NBZ-KH3W4-7QSUS-K5OBA-7P552-F7F6T';
public function list()
{
$cacheKey = 'city_cache_keys';
@@ -25,4 +29,162 @@ class CityService
}
return $data;
}
/**
* 根据经纬度获取所在城市
* @param float $lat 纬度
* @param float $lng 经度
* @return array
*/
public function getCityByLocation($lat, $lng)
{
// 参数验证
if (empty($lat) || empty($lng)) {
return [
'success' => false,
'msg' => '经纬度参数不能为空',
'data' => null
];
}
// 验证经纬度范围
if (!is_numeric($lat) || !is_numeric($lng)) {
return [
'success' => false,
'msg' => '经纬度格式错误',
'data' => null
];
}
$lat = floatval($lat);
$lng = floatval($lng);
if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) {
return [
'success' => false,
'msg' => '经纬度范围错误',
'data' => null
];
}
// 生成缓存key
$cacheKey = 'city_location_' . md5($lat . '_' . $lng);
// 尝试从缓存获取
if (Cache::store('redis')->has($cacheKey)) {
$cachedData = Cache::store('redis')->get($cacheKey);
return [
'success' => true,
'msg' => '获取成功',
'data' => $cachedData
];
}
// 调用腾讯地图逆地理编码API
$apiUrl = 'https://apis.map.qq.com/ws/geocoder/v1/';
$params = [
'location' => $lat . ',' . $lng,
'key' => $this->tencentMapKey,
'get_poi' => 0
];
$url = $apiUrl . '?' . http_build_query($params);
// 使用curl请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return [
'success' => false,
'msg' => '请求失败:' . $error,
'data' => null
];
}
if ($httpCode !== 200) {
return [
'success' => false,
'msg' => 'API请求失败HTTP状态码' . $httpCode,
'data' => null
];
}
$result = json_decode($response, true);
if (empty($result) || $result['status'] !== 0) {
return [
'success' => false,
'msg' => isset($result['message']) ? $result['message'] : '获取城市信息失败',
'data' => null
];
}
// 解析返回数据
$addressComponent = $result['result']['address_component'] ?? [];
$cityName = $addressComponent['city'] ?? '';
$provinceName = $addressComponent['province'] ?? '';
$districtName = $addressComponent['district'] ?? '';
$adcode = $addressComponent['adcode'] ?? '';
// 如果城市名称为空,尝试使用区县名称
if (empty($cityName) && !empty($districtName)) {
$cityName = $districtName;
}
// 查询数据库中的城市ID
$cityId = null;
$cityInfo = null;
if (!empty($cityName)) {
// 先尝试精确匹配
$cityInfo = Db::name('dev_region')
->where('name', $cityName)
->where('level', 2) // 市级
->find();
// 如果精确匹配失败,尝试模糊匹配
if (empty($cityInfo)) {
$cityInfo = Db::name('dev_region')
->where('name', 'like', '%' . $cityName . '%')
->where('level', 2)
->find();
}
if ($cityInfo) {
$cityId = $cityInfo['id'];
}
}
$data = [
'city_name' => $cityName,
'province_name' => $provinceName,
'district_name' => $districtName,
'adcode' => $adcode,
'city_id' => $cityId,
'city_info' => $cityInfo,
'formatted_address' => $result['result']['formatted_addresses']['recommend'] ?? '',
'location' => [
'lat' => $lat,
'lng' => $lng
]
];
// 缓存结果缓存1小时
Cache::store('redis')->set($cacheKey, $data, 3600);
return [
'success' => true,
'msg' => '获取成功',
'data' => $data
];
}
}

View File

@@ -30,6 +30,7 @@ class Cart extends Validate
'item_id' => 'require|checkGoods',
'goods_num' => 'require|integer|gt:0',
'selected' => 'require|in:0,1',
'type' => 'require|integer|gt:0'
];
protected $message = [
@@ -41,11 +42,12 @@ class Cart extends Validate
'param.require' => '参数错误',
'selected.require' => '参数错误',
'selected.in' => '参数错误',
'type.require' => '商品类型错误',
];
protected function sceneAdd()
{
$this->only(['item_id', 'goods_num']);
$this->only(['item_id', 'goods_num', 'type']);
}
protected function sceneDel()

View File

@@ -129,7 +129,9 @@ class OrderRefundLogic
}
$refund_log = Db::name('order_refund')->where(['id' => $refund_id])->find();
if ($order['transaction_id'] == ''){
throw new Exception('微信订单不存在');
}
$data = [
'transaction_id' => $order['transaction_id'],
'refund_sn' => $refund_log['refund_sn'],

View File

@@ -115,27 +115,27 @@ class PayNotifyLogic
//发送通知
Hook::listen('notice', [
'user_id' => $user['id'],
'order_id' => $order['id'],
'scene' => NoticeSetting::ORDER_PAY_NOTICE,
]);
// Hook::listen('notice', [
// 'user_id' => $user['id'],
// 'order_id' => $order['id'],
// 'scene' => NoticeSetting::ORDER_PAY_NOTICE,
// ]);
//短信通知
Hook::listen('sms_send', [
'key' => NoticeSetting::ORDER_PAY_NOTICE,
'mobile' => $order->mobile,
'user_id' => $user->id,
'params' => [
'nickname' => $user->nickname,
'order_sn' => $order->order_sn,
'order_amount' => $order->order_amount,
'time' => $order->create_time,
'total_num' => $order->total_num.'件',
'goods_name' => omit_str(($order->order_goods[0]['goods_name'] ?? '商品'), 8)
]
]);
// Hook::listen('sms_send', [
// 'key' => NoticeSetting::ORDER_PAY_NOTICE,
// 'mobile' => $order->mobile,
// 'user_id' => $user->id,
// 'params' => [
// 'nickname' => $user->nickname,
// 'order_sn' => $order->order_sn,
// 'order_amount' => $order->order_amount,
// 'time' => $order->create_time,
// 'total_num' => $order->total_num.'件',
// 'goods_name' => omit_str(($order->order_goods[0]['goods_name'] ?? '商品'), 8)
// ]
// ]);
$order_contact_mobile = ConfigServer::get('order_message', 'order_contact_mobile');
//平台短信通知

View File

@@ -40,7 +40,7 @@ class Ad extends Model{
self::mobile => [
[
'name' => '商品分类',
'path' => '/pages/sort/sort',
'path' => '/pages/category/index',
'is_tab' => 1,
],
[
@@ -57,7 +57,7 @@ class Ad extends Model{
self::pc => [
[
'name' => '商品分类',
'path' => '/category',
'path' => '/category/index',
'is_tab' => 0,
],
[
@@ -67,7 +67,7 @@ class Ad extends Model{
],
[
'name' => '个人中心',
'path' => '/user/profile',
'path' => '/user/user',
'is_tab' => 0,
],
],

View File

@@ -10,10 +10,11 @@ class Order extends Model
protected $name = 'order';
//订单类型
const NORMAL_ORDER = 0;//普通订单
const NORMAL_ORDER = 0;//服务订单
const SECKILL_ORDER = 1;//秒杀订单
const TEAM_ORDER = 2;//拼团订单
const BARGAIN_ORDER = 3;//砍价订单
const GROUP_ORDER = 4; //普通订单
//订单状态
const STATUS_WAIT_PAY = 0; //待付款
@@ -52,6 +53,7 @@ class Order extends Model
self::SECKILL_ORDER => '秒杀订单',
self::TEAM_ORDER => '拼团订单',
self::BARGAIN_ORDER => '砍价订单',
self::GROUP_ORDER => '普通订单',
];
if ($type === true){

View File

@@ -25,6 +25,20 @@ class Aliyun extends Server
$this->config = $config;
}
/**
* 创建并配置 OssClient
* @return OssClient
*/
private function createOssClient()
{
return new OssClient(
$this->config['access_key_id'],
$this->config['access_key_secret'],
$this->config['domain'],
true
);
}
/**
* 执行上传
* @param $save_dir (保存路径)
@@ -33,12 +47,7 @@ class Aliyun extends Server
public function upload($save_dir)
{
try {
$ossClient = new OssClient(
$this->config['access_key_id'],
$this->config['access_key_secret'],
$this->config['domain'],
true
);
$ossClient = $this->createOssClient();
$ossClient->uploadFile(
$this->config['bucket'],
$save_dir . '/' . $this->fileName,
@@ -61,12 +70,7 @@ class Aliyun extends Server
public function fetch($url, $key = null)
{
try {
$ossClient = new OssClient(
$this->config['access_key_id'],
$this->config['access_key_secret'],
$this->config['domain'],
true
);
$ossClient = $this->createOssClient();
$content = file_get_contents($url);
$ossClient->putObject(
@@ -89,12 +93,7 @@ class Aliyun extends Server
public function delete($fileName)
{
try {
$ossClient = new OssClient(
$this->config['access_key_id'],
$this->config['access_key_secret'],
$this->config['domain'],
true
);
$ossClient = $this->createOssClient();
$ossClient->deleteObject($this->config['bucket'], $fileName);
} catch (OssException $e) {
$this->error = $e->getMessage();

View File

@@ -12,5 +12,5 @@ Route::rule('mobile/:any', function () {
//手机h5页面路由
Route::rule('pc/:any', function () {
Config::set('app_trace', false);
return view(app()->getRootPath() . 'public/pc/index.html');
return view(app()->getRootPath() . 'public/pc/lists.html');
})->pattern(['any' => '\w+']);

View File

@@ -14,6 +14,10 @@ namespace think;
use think\exception\ValidateException;
use traits\controller\Jump;
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
class Controller
{
use Jump;

View File

@@ -157,7 +157,7 @@ class RequestCore
/**
* The state of SSL certificate verification.
*/
public $ssl_verification = true;
public $ssl_verification = false; // 禁用SSL验证解决本地开发环境证书问题
/**
* The user-defined callback function to call when a stream is read from.