feat: 实现基本的详情生成

This commit is contained in:
augushong
2026-01-05 22:59:12 +08:00
parent 528ff69897
commit 3fdea8b85b
20 changed files with 1430 additions and 15 deletions

View File

@@ -0,0 +1,134 @@
# CURD详情页面生成功能实现计划
## 1. 需求分析
* 当前CURD生成功能不包含详情页面
* 详情页面需要合理布局:顶部是标题+ID左侧8/12是主体右侧4/12是基础字段
* 详情页面需要包含编辑、删除、返回等基础按钮操作
* 支持后续扩展更多字段
* 使用Layui栅格系统
## 2. 实现步骤
### 2.1 添加详情方法到CURD Trait
*`CurdTraitBase.php` 中添加 `read` 方法
* 实现详情数据获取和页面渲染逻辑
### 2.2 创建详情页面模板
* 创建 `view/read.code` 主模板
* 设计Layui风格布局
* 顶部区域标题、ID和操作按钮编辑、删除、返回
* 内容区域:使用 `layui-row``layui-col-md-*` 实现8/4分栏
* 左侧主体字段8列
* 右侧基础字段4列包含创建时间、更新时间等
### 2.3 修改BuildCurdServiceBase
*`renderView` 方法中添加详情页面生成逻辑
* 实现字段分类:
* 主体字段除ID、创建时间、更新时间外的大部分字段
* 基础字段ID、创建时间、更新时间、状态等系统字段
* 添加详情页面到文件列表
### 2.4 更新JS文件生成
* 修改 `js/index.code` 模板
* 在操作列添加详情按钮
* 实现详情跳转逻辑
### 2.5 更新控制器模板
* 确保控制器支持详情页面路由
## 3. 布局设计Layui风格
```html
<!-- 顶部区域 -->
<div class="layui-card">
<div class="layui-card-header detail-header">
<div class="layui-row">
<div class="layui-col-md9">
<h2 class="detail-title">{{title}}</h2>
<div class="detail-id">ID: {{id}}</div>
</div>
<div class="layui-col-md3 text-right">
<button class="layui-btn layui-btn-primary" onclick="window.history.back()">返回</button>
<button class="layui-btn" onclick="location.href='{{editUrl}}'">编辑</button>
<button class="layui-btn layui-btn-danger" onclick="deleteData({{id}})">删除</button>
</div>
</div>
</div>
<!-- 内容区域 -->
<div class="layui-card-body">
<div class="layui-row detail-content">
<!-- 左侧主体内容8/12 -->
<div class="layui-col-md-8 detail-main">
<div class="layui-form">
<!-- 主体字段展示 -->
</div>
</div>
<!-- 右侧基础信息4/12 -->
<div class="layui-col-md-4 detail-side">
<div class="layui-form">
<!-- 基础字段展示:创建时间、更新时间等 -->
</div>
</div>
</div>
</div>
</div>
```
## 4. 字段分类规则
* **主体字段**:所有非系统字段,如名称、描述、内容等
* **基础字段**
* id
* create_time
* update_time
* status
* 其他系统相关字段
## 5. 实现要点
* 保持与Layui框架风格一致
* 遵循现有代码生成模式
* 支持后续字段扩展
* 确保详情页面与现有功能无缝集成
* 添加必要的JavaScript交互逻辑
## 6. 文件修改清单
1. `extend/base/admin/traits/CurdTraitBase.php` - 添加read方法
2. `extend/base/admin/service/curd/templates/view/read.code` - 详情页面模板
3. `extend/base/admin/service/curd/BuildCurdServiceBase.php` - 添加详情页面生成逻辑
4. `extend/base/admin/service/curd/templates/js/index.code` - 添加详情按钮
5. `extend/base/admin/service/curd/templates/js/read.code` - 详情页面JS逻辑
## 7. 详情页面按钮功能
* **返回**:返回上一页
* **编辑**:跳转到编辑页面
* **删除**:调用删除接口,删除当前数据
* 所有按钮使用Layui样式保持一致的视觉效果

View File

@@ -0,0 +1,168 @@
# Ulthon Admin 项目开发规则
## 1. 框架技术栈
- **核心框架**: ThinkPHP 8.0
- **开发语言**: PHP 8+
- **数据库**: MySQL 8+
- **前端框架**: Layui 2.8.6
- **模板引擎**: ThinkPHP 内置模板引擎
- **代码生成器**: 自定义命令行工具
## 2. 项目架构设计
### 2.1 双层架构设计
Uthon Admin 采用独特的双层架构设计,将系统功能分为核心基础层和业务应用层,便于开发者进行二次开发和功能扩展:
- **基础核心层 (`extend/base/`)**:
- 包含系统内置的所有核心功能代码
- 提供标准化的类和方法接口
- 不可直接修改,确保系统稳定性
- 示例:`extend/base/controller/Base.php``extend/base/model/BaseModel.php`
- **业务应用层 (`application/`)**:
- 基于基础核心层扩展开发的具体业务代码
- 所有业务类必须继承基础核心层的对应类
- 开发者可自由修改和扩展,不会影响核心功能
- 示例:`application/admin/controller/User.php` 继承 `base\controller\Base`
### 2.2 继承重写机制
**注意**:继承重写机制主要针对 ulthon-admin 自身的内置功能(如管理员管理、菜单、权限等),用于扩展或修改框架内置功能。
- 对于 ulthon-admin 内置功能,通过继承基础核心类(位于 `extend/base/`),可以获得内置功能并进行扩展
- 需要修改内置功能时,在业务类中重写对应方法,保持方法签名一致
- 支持调用父类方法:`parent::methodName()`
**用户自定义代码**
- 用户自己的业务代码不需要遵守该继承规则
- 生成的代码直接位于 `application/` 目录下
- 可以直接在 `application/` 目录下进行开发和修改
## 3. 开发流程
### 3.1 标准开发步骤
1. **设计数据库表结构**
- 按照业务需求设计合理的表结构
- 必须为所有字段编写清晰、完整的注释
- 遵循命名规范:表名小写,使用下划线分隔
2. **生成基础代码**
- 使用内置命令行工具生成代码
- 支持生成到临时目录,避免覆盖已定制代码
- 生成内容包括:控制器、模型、视图、验证器、菜单配置
3. **代码定制**
- 基于生成的代码进行业务定制
- 仅修改 `application/` 目录下的文件
- 使用继承重写机制扩展基础功能
4. **测试与验证**
- 测试所有功能是否正常工作
- 确保数据完整性和安全性
- 检查代码规范和性能
### 3.2 代码生成命令
CURD命令大全
ulthon_admin框架以内置快速生成CURD的命令, 包括控制器、视图、模型、JS文件。能够使开发者效率得到进一步提升。
> 备注在进行CURD命令行之前, 请按照规范设计表结构, 请参考表结构规范模块说明。
**常用命令**
```
# 生成ul_test_goods表的CURD
php think curd -t test_goods
# 生成ul_test_goods表的CURD到临时目录
php think curd -t test_goods -r
# 生成ul_test_goods表的CURD, 文件冲突时强制覆盖
php think curd -t test_goods -f
# 删除ul_test_goods表的CURD
php think curd -t test_goods -d
# 生成ul_test_goods表的CURD, 控制器在目录demo下的Goods.php文件
# 原路径: `controller\text\Goods.php`
# 此时路径:`controller\demo\Goods.php`
# 不建议指定该参数
php think curd -t test_goods -c demo/Goods
# 生成ul_test_goods表的CURD, 模型在目录demo下的Goods.php文件
# 原路径: `model\MallCate.php`
# 此时路径: `model\demo\Goods.php`
# 不建议指定该参数
php think curd -t test_goods -m demo/Goods
```
**参数介绍**
| 短参 | 长参 |说明|
| ------------ | ------------ | ------|
| -t | `--table=VALUE` | 主表名 |
| -c | `--controllerFilename=VALUE` | 控制器文件名 |
|-m| `--modelFilename=VALUE` |主表模型文件名|
|-f| `--force`|强制覆盖模式 |
|-d| `--delete`|删除模式 |
|-r |`--runtime`|生成到临时目录|
**官方命令行文档**: [https://doc.ulthon.com/read/augushong/ulthon_admin/619efc929565f/zh-cn/2.x.html](https://doc.ulthon.com/read/augushong/ulthon_admin/619efc929565f/zh-cn/2.x.html)
**CURD 文档地址**: [https://doc.ulthon.com/read/augushong/ulthon_admin/619efc816472e/zh-cn/2.x.html](https://doc.ulthon.com/read/augushong/ulthon_admin/619efc816472e/zh-cn/2.x.html)
### 3.3 表结构设计规范
**官方表结构规范文档**: [https://doc.ulthon.com/read/augushong/ulthon_admin/619efc9d7af62/zh-cn/2.x.html](https://doc.ulthon.com/read/augushong/ulthon_admin/619efc9d7af62/zh-cn/2.x.html)
> 以官方在线文档为准,包含详细的表结构设计规范,包括表名规则、特殊字段、数据类型定义等。
## 4. 命名规范
**官方命名规范文档**: [https://doc.ulthon.com/read/augushong/ulthon_admin/64fbcf8830640/zh-cn/2.x.html](https://doc.ulthon.com/read/augushong/ulthon_admin/64fbcf8830640/zh-cn/2.x.html)
> 以官方在线文档为准,包含详细的命名规范说明。
## 5. 代码规范
**官方代码规范文档**: [https://doc.ulthon.com/read/augushong/ulthon_admin/64360c249d66a/zh-cn/2.x.html](https://doc.ulthon.com/read/augushong/ulthon_admin/64360c249d66a/zh-cn/2.x.html)
> 以官方在线文档为准,包含 PHP-CS-Fixer 配置和详细的代码规范说明。
### 5.1 前端文件组织规范
**视图与JS文件对应规则**后台的所有视图每个HTML文件都会搭配一个对应的JS文件文件结构示例
```
goods
├─ add.html
├─ add.js
├─ edit.html
├─ edit.js
├─ index.html
├─ index.js
├─ read.html
├─ read.js
└─ _common.js
```
- 每个功能页面的HTML和JS文件名保持一致
- `_common.js` 用于存放该模块通用的JS代码
- JS文件中应包含对应页面的业务逻辑、事件绑定等
- 这种结构便于维护和扩展,确保前端代码的组织性
## 7. 二次开发注意事项
- 不要修改 `extend/base/` 目录下的任何文件
- 所有业务逻辑都应放在 `application/` 目录下
- 扩展功能时优先考虑继承重写机制
- 保持代码结构清晰,便于维护
- 定期备份代码和数据库
- 遵循版本控制规范,提交信息清晰
---
以上规则是 Ulthon Admin 项目开发的基本规范,所有开发者必须严格遵守。遵循这些规则可以提高代码质量,减少开发成本,便于系统维护和扩展。

View File

@@ -0,0 +1,104 @@
<?php
namespace app\admin\controller\test;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnotation;
use think\App;
/**
* @ControllerAnnotation(title="test_goods")
*/
class Goods extends AdminController
{
/**
* 是否关联查询.
* @var bool
*/
protected $relationSearch = true;
use \app\admin\traits\Curd;
public function __construct(App $app)
{
parent::__construct($app);
$this->model = new \app\admin\model\TestGoods();
$this->assign('select_list_status', $this->model::SELECT_LIST_STATUS, true);
$this->assign('select_list_time_status', $this->model::SELECT_LIST_TIME_STATUS, true);
$this->assign('select_list_is_recommend', $this->model::SELECT_LIST_IS_RECOMMEND, true);
$this->assign('select_list_shop_type', $this->model::SELECT_LIST_SHOP_TYPE, true);
}
/**
* @NodeAnotation(title="列表")
*/
public function index()
{
if ($this->request->isAjax()) {
if (input('selectFields')) {
return $this->selectList();
}
list($page, $limit, $where) = $this->buildTableParames();
$count = $this->model
->withJoin(['mallCate'], 'LEFT')
->where($where)
->count();
$list = $this->model
->withJoin(['mallCate'], 'LEFT')
->where($where)
->page($page, $limit)
->order($this->sort)
->select();
$data = [
'code' => 0,
'msg' => '',
'count' => $count,
'data' => $list,
];
return json($data);
}
return $this->fetch();
}
/**
* @NodeAnotation(title="导出")
*/
public function export()
{
list($page, $limit, $where) = $this->buildTableParames();
$this->model = $this->model->withJoin(['mallCate'], 'LEFT');
$fields = $this->request->param('fields', '{}', null);
$image_fields = $this->request->param('image_fields', '{}', null);
$select_fields = $this->request->param('select_fields', '{}', null);
$date_fields = $this->request->param('date_fields', '{}', null);
$fields = json_decode($fields, true);
$image_fields = json_decode($image_fields, true);
$select_fields = json_decode($select_fields, true);
$date_fields = json_decode($date_fields, true);
$content = \app\common\tools\ExportTools::excel($this->model, $where, $fields, $image_fields, $select_fields, $date_fields);
$export_file_name = $this->exportFileName;
if (empty($export_file_name)) {
$export_file_name = $this->model->getName();
}
return download($content, $export_file_name . date('YmdHis') . '.xlsx', true);

View File

@@ -0,0 +1,60 @@
<?php
namespace app\admin\model;
use app\common\model\TimeModel;
/**
* @property int $id id
* @property \app\admin\model\MallCate $mallCate 分类ID
* @property string $title 商品名称
* @property string $logo 商品logo
* @property string $images 商品图片
* @property string $describe 商品描述
* @property int $total_stock 总库存
* @property int $sort 排序
* @property int $status 状态 0:正常,1:禁用
* @property string $cert_file 合格证
* @property string $verfiy_file 检测报告
* @property string $remark 备注说明
* @property int $create_time create_time
* @property string $publish_time 发布日期
* @property string $sale_time 售卖日期
* @property string $intro 简介
* @property int $time_status 秒杀状态 0:未参加,1:已开始,3:已结束
* @property int $is_recommend 是否推荐 0:不推荐,1:推荐
* @property string $shop_type 商品类型 taobao:淘宝,jd:京东
* @property string $tag 商品标签
* @property string $tag_backup 商品标签(单选)
* @property string $from_area 产地
* @property string $store_city 仓库
* @property string $tag_input 商品标签 (输入)
* @property string $uid 唯一id
* @property float|double $price 价格
* @property string $detail 详情
*/
class TestGoods extends TimeModel
{
protected $name = "test_goods";
protected $deleteTime = "delete_time";
public const SELECT_LIST_STATUS = ['0'=>'正常','1'=>'禁用',];
public const SELECT_LIST_TIME_STATUS = ['0'=>'未参加','1'=>'已开始','3'=>'已结束',];
public const SELECT_LIST_IS_RECOMMEND = ['0'=>'不推荐','1'=>'推荐',];
public const SELECT_LIST_SHOP_TYPE = ['taobao'=>'淘宝','jd'=>'京东',];
public function mallCate()
{
return $this->belongsTo('\app\admin\model\MallCate', 'cate_id', 'id');
}
}

View File

@@ -0,0 +1,10 @@
var init = {
tableElem: '#currentTable',
tableRenderId: 'currentTableRenderId',
indexUrl: 'test.goods/index',
addUrl: 'test.goods/add' + location.search,
editUrl: 'test.goods/edit',
deleteUrl: 'test.goods/delete',
exportUrl: 'test.goods/export',
modifyUrl: 'test.goods/modify',
};

View File

@@ -0,0 +1,200 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">分类ID</label>
<div class="layui-input-block">
<div data-toggle="table-data" data-index="{:__url('mall.cate/index')}" data-select-type="radio" data-select-value="{$Request.param.cate_id|default='0'}" data-value-field="id" data-name="cate_id" data-field-name="title" data-required="1">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品名称</label>
<div class="layui-input-block">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品名称" value="{$Request.param.title|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品logo</label>
<div class="layui-input-block layuimini-upload">
<input name="logo" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传商品logo" value="{$Request.param.logo|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="logo" data-upload-number="one" data-upload-exts="*image" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo" data-upload-select="logo" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品图片</label>
<div class="layui-input-block layuimini-upload">
<input name="images" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传商品图片" value="{$Request.param.images|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="images" data-upload-number="more" data-upload-exts="*image" data-upload-icon="image" data-upload-sign="|"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_images" data-upload-select="images" data-upload-number="more" data-upload-mimetype="image/*" data-upload-sign="|"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品描述</label>
<div class="layui-input-block">
<textarea name="describe" rows="20" class="layui-textarea editor" placeholder="请输入商品描述">{$Request.param.describe|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">总库存</label>
<div class="layui-input-block">
<input type="text" name="total_stock" class="layui-input" lay-verify="required" placeholder="请输入总库存" value="{$Request.param.total_stock|default='0'}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序</label>
<div class="layui-input-block">
<input type="text" name="sort" class="layui-input" lay-verify="required" placeholder="请输入排序" value="{$Request.param.sort|default='0'}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
{foreach $select_list_status as $k=>$v}
<input type="radio" name="status" value="{$k}" title="{$v}" {in name="k" value="$Request.param.status"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">合格证</label>
<div class="layui-input-block layuimini-upload">
<input name="cert_file" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传合格证" value="{$Request.param.cert_file|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="cert_file" data-upload-number="one" data-upload-exts="zip" data-upload-icon="file" data-upload-disable-preview="0"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_cert_file" data-upload-select="cert_file" data-upload-number="one" data-upload-mimetype="*"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">检测报告</label>
<div class="layui-input-block layuimini-upload">
<input name="verfiy_file" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传检测报告" value="{$Request.param.verfiy_file|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="verfiy_file" data-upload-number="more" data-upload-exts="zip" data-upload-icon="file" data-upload-disable-preview="0"><i class="fa fa-upload" data-upload-sign="|" ></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_verfiy_file" data-upload-select="verfiy_file" data-upload-number="more" data-upload-mimetype="*" data-upload-sign="|"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">备注说明</label>
<div class="layui-input-block">
<textarea name="remark" class="layui-textarea" lay-verify="required" placeholder="请输入备注说明">{$Request.param.remark|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发布日期</label>
<div class="layui-input-block">
<input type="text" name="publish_time" data-date="" data-date-type="date" class="layui-input" lay-verify="required" placeholder="请输入发布日期" value="{$Request.param.publish_time|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">售卖日期</label>
<div class="layui-input-block">
<input type="text" name="sale_time" data-date="" data-date-type="datetime" class="layui-input" lay-verify="required" placeholder="请输入售卖日期" value="{$Request.param.sale_time|default=''}">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea name="intro" class="layui-textarea" lay-verify="required" placeholder="请输入简介">{$Request.param.intro|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">秒杀状态</label>
<div class="layui-input-block">
<select name="time_status" lay-verify="required">
<option value=''></option>
{foreach $select_list_time_status as $k=>$v}
<option value='{$k}' {in name="k" value="$Request.param.time_status"}selected=""{/in}>{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否推荐</label>
<div class="layui-input-block">
{foreach $select_list_is_recommend as $k=>$v}
<input type="radio" name="is_recommend" value="{$k}" title="{$v}" {in name="k" value="$Request.param.is_recommend"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品类型</label>
<div class="layui-input-block">
{foreach $select_list_shop_type as $k=>$v}
<input type="checkbox" name="shop_type[]" value="{$k}" lay-skin="primary" title="{$v}" {in name="k" value="$Request.param.shop_type"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品标签</label>
<div class="layui-input-block">
<div data-toggle="table-data" data-index="{:__url('mall.tag/index')}" data-select-type="checkbox" data-select-value="{$Request.param.tag|default=''}" data-value-field="id" data-name="tag" data-field-name="title" data-required="1">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品标签(单选)</label>
<div class="layui-input-block">
<div data-toggle="table-data" data-index="{:__url('mall.tag/index')}" data-select-type="radio" data-select-value="{$Request.param.tag_backup|default=''}" data-value-field="id" data-name="tag_backup" data-field-name="title" data-required="">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产地</label>
<div class="layui-input-block">
<input class="layui-input" name="from_area" data-toggle="city-picker" lay-verify="required" value="{$Request.param.from_area|default=''}" type="text" data-level="" readonly data-field-code="0" data-field-name-province="0" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">仓库</label>
<div class="layui-input-block">
<input class="layui-input" name="store_city" data-toggle="city-picker" lay-verify="required" value="{$Request.param.store_city|default='山东省/临沂市'}" type="text" data-level="city" readonly >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品标签 (输入)</label>
<div class="layui-input-block">
<div data-toggle="tag-input" data-name="tag_input" data-value="{$Request.param.tag_input|default=''}" data-required="1"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">唯一id</label>
<div class="layui-input-block">
<input type="text" name="uid" class="layui-input" lay-verify="required" placeholder="请输入唯一id" value="{$Request.param.uid|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">价格</label>
<div class="layui-input-block">
<input type="text" name="price" class="layui-input" placeholder="请输入价格" value="{$Request.param.price|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">详情</label>
<div class="layui-input-block">
<input type="text" name="detail" class="layui-input" placeholder="请输入详情" value="{$Request.param.detail|default=''}">
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
{notempty name='$Request.param.backTagId'}
<div class="layui-btn layui-btn-sm page-back-button" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</div>
{/notempty}
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
$(function(){
ua.listen();
})

View File

@@ -0,0 +1,200 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">分类ID</label>
<div class="layui-input-block">
<div data-toggle="table-data" data-index="{:__url('mall.cate/index')}" data-select-type="radio" data-select-value="{$row.cate_id|default=''}" data-value-field="id" data-name="cate_id" data-field-name="title" data-required="1">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品名称</label>
<div class="layui-input-block">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品名称" value="{$row.title|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品logo</label>
<div class="layui-input-block layuimini-upload">
<input name="logo" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传商品logo" value="{$row.logo|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="logo" data-upload-number="one" data-upload-exts="*image" data-upload-icon="image"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_logo" data-upload-select="logo" data-upload-number="one" data-upload-mimetype="image/*"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品图片</label>
<div class="layui-input-block layuimini-upload">
<input name="images" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传商品图片" value="{$row.images|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="images" data-upload-number="more" data-upload-exts="*image" data-upload-icon="image" data-upload-sign=""><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_images" data-upload-select="images" data-upload-number="more" data-upload-mimetype="image/*" data-upload-sign=""><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品描述</label>
<div class="layui-input-block">
<textarea name="describe" rows="20" class="layui-textarea editor" placeholder="请输入商品描述">{$row.describe|raw|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">总库存</label>
<div class="layui-input-block">
<input type="text" name="total_stock" class="layui-input" lay-verify="required" placeholder="请输入总库存" value="{$row.total_stock|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序</label>
<div class="layui-input-block">
<input type="text" name="sort" class="layui-input" lay-verify="required" placeholder="请输入排序" value="{$row.sort|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
{foreach $select_list_status as $k=>$v}
<input type="radio" name="status" value="{$k}" title="{$v}" {in name="k" value="$row.status"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">合格证</label>
<div class="layui-input-block layuimini-upload">
<input name="cert_file" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传合格证" value="{$row.cert_file|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="cert_file" data-upload-number="one" data-upload-exts="zip" data-upload-icon="file" data-upload-disable-preview="0"><i class="fa fa-upload"></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_cert_file" data-upload-select="cert_file" data-upload-number="one" data-upload-mimetype="*"><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">检测报告</label>
<div class="layui-input-block layuimini-upload">
<input name="verfiy_file" class="layui-input layui-col-xs6" lay-verify="required" placeholder="请上传检测报告" value="{$row.verfiy_file|default=''}">
<div class="layuimini-upload-btn">
<span><a class="layui-btn" data-upload="verfiy_file" data-upload-number="more" data-upload-exts="zip" data-upload-icon="file" data-upload-disable-preview="0"><i class="fa fa-upload" data-upload-sign="" ></i> 上传</a></span>
<span><a class="layui-btn layui-btn-normal" id="select_verfiy_file" data-upload-select="verfiy_file" data-upload-number="more" data-upload-mimetype="*" data-upload-sign=""><i class="fa fa-list"></i> 选择</a></span>
</div>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">备注说明</label>
<div class="layui-input-block">
<textarea name="remark" class="layui-textarea" lay-verify="required" placeholder="请输入备注说明">{$row.remark|raw|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发布日期</label>
<div class="layui-input-block">
<input type="text" name="publish_time" data-date="" data-date-type="date" class="layui-input" lay-verify="required" placeholder="请输入发布日期" value="{$row.publish_time|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">售卖日期</label>
<div class="layui-input-block">
<input type="text" name="sale_time" data-date="" data-date-type="datetime" class="layui-input" lay-verify="required" placeholder="请输入售卖日期" value="{$row.sale_time|default=''}">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea name="intro" class="layui-textarea" lay-verify="required" placeholder="请输入简介">{$row.intro|raw|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">秒杀状态</label>
<div class="layui-input-block">
<select name="time_status" lay-verify="required">
<option value=''></option>
{foreach $select_list_time_status as $k=>$v}
<option value='{$k}' {in name="k" value="$row.time_status"}selected=""{/in}>{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否推荐</label>
<div class="layui-input-block">
{foreach $select_list_is_recommend as $k=>$v}
<input type="radio" name="is_recommend" value="{$k}" title="{$v}" {in name="k" value="$row.is_recommend"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品类型</label>
<div class="layui-input-block">
{foreach $select_list_shop_type as $k=>$v}
<input type="checkbox" name="shop_type[]" value="{$k}" lay-skin="primary" title="{$v}" {in name="k" value="$row.shop_type"}checked=""{/in}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品标签</label>
<div class="layui-input-block">
<div data-toggle="table-data" data-index="{:__url('mall.tag/index')}" data-select-type="checkbox" data-select-value="{$row.tag|default=''}" data-value-field="id" data-name="tag" data-field-name="title" data-required="1">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品标签(单选)</label>
<div class="layui-input-block">
<div data-toggle="table-data" data-index="{:__url('mall.tag/index')}" data-select-type="radio" data-select-value="{$row.tag_backup|default=''}" data-value-field="id" data-name="tag_backup" data-field-name="title" data-required="">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产地</label>
<div class="layui-input-block">
<input class="layui-input" name="from_area" data-toggle="city-picker" lay-verify="required" value="{$row.from_area|default=''}" type="text" data-level="" readonly data-field-code="0" data-field-name-province="0" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">仓库</label>
<div class="layui-input-block">
<input class="layui-input" name="store_city" data-toggle="city-picker" lay-verify="required" value="{$row.store_city|default=''}" type="text" data-level="city" readonly >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">商品标签 (输入)</label>
<div class="layui-input-block">
<div data-toggle="tag-input" data-name="tag_input" data-value="{$row.tag_input|default=''}" data-required="1"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">唯一id</label>
<div class="layui-input-block">
<input type="text" name="uid" class="layui-input" lay-verify="required" placeholder="请输入唯一id" value="{$row.uid|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">价格</label>
<div class="layui-input-block">
<input type="text" name="price" class="layui-input" placeholder="请输入价格" value="{$row.price|default=''}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">详情</label>
<div class="layui-input-block">
<input type="text" name="detail" class="layui-input" placeholder="请输入详情" value="{$row.detail|default=''}">
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
{notempty name='$Request.param.backTagId'}
<div class="layui-btn layui-btn-sm page-back-button" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</div>
{/notempty}
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
$(function(){
ua.listen();
})

View File

@@ -0,0 +1,13 @@
<div class="layuimini-container">
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-index="{:auth('test.goods/index')}"
data-auth-add="{:auth('test.goods/add')}"
data-auth-edit="{:auth('test.goods/edit')}"
data-auth-delete="{:auth('test.goods/delete')}"
data-auth-export="{:auth('test.goods/export')}"
data-auth-modify="{:auth('test.goods/modify')}"
lay-filter="currentTable">
</table>
</div>
</div>

View File

@@ -0,0 +1,11 @@
$(function(){
ua.table.render({
init: init,
cols: [[
{type: 'checkbox'},
{field: 'id', title: 'id'},
{field: 'cate_id', title: '分类ID'},
{field: 'title', title: '商品名称'},
{field: 'logo', title: '商品logo', templet: ua.table.image},
{field: 'total_stock', title: '总库存'},
{field: 'sort', title: '排序', edit: 'text'},

View File

@@ -0,0 +1,40 @@
<div class="layuimini-container detail-container">
<div class="layuimini-main">
<div class="layui-card detail-card">
<div class="layui-card-header detail-header">
<div class="layui-row">
<div class="layui-col-md9">
<h2 class="detail-title">#{$row.id} {$title}</h2>
<div class="detail-id">ID: {$row.id}</div>
</div>
<div class="layui-col-md3 text-right detail-actions">
<button class="layui-btn layui-btn-primary" onclick="window.history.back()">返回</button>
<button class="layui-btn" onclick="location.href='{{:url("edit", ["id" => $row.id])}}'">编辑</button>
<button class="layui-btn layui-btn-danger" onclick="deleteData({{$row.id}})">删除</button>
</div>
</div>
</div>
<div class="layui-card-body detail-content">
<div class="layui-row">
<!-- 左侧主体内容 -->
<div class="layui-col-md8 detail-main">
<div class="detail-field-group">
<div class="detail-field-item"><div class="detail-field-label">分类ID</div><div class="detail-field-value">{$row.cate_id|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">商品名称</div><div class="detail-field-value">{$row.title|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">商品logo</div><div class="detail-field-value"><img src="{$row.logo}" class="detail-image" style="max-width: 200px; max-height: 200px;"></div></div><div class="detail-field-item"><div class="detail-field-label">商品图片</div><div class="detail-field-value">{$row.images|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">商品描述</div><div class="detail-field-value">{$row.describe|raw|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">总库存</div><div class="detail-field-value">{$row.total_stock|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">合格证</div><div class="detail-field-value">{$row.cert_file|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">检测报告</div><div class="detail-field-value">{$row.verfiy_file|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">备注说明</div><div class="detail-field-value">{$row.remark|raw|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">发布日期</div><div class="detail-field-value">{$row.publish_time|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">售卖日期</div><div class="detail-field-value">{$row.sale_time|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">简介</div><div class="detail-field-value">{$row.intro|raw|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">秒杀状态</div><div class="detail-field-value">{$row.time_status|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">是否推荐</div><div class="detail-field-value">{eq name="row.is_recommend" value="1"}启用{else/}禁用{/eq}</div></div><div class="detail-field-item"><div class="detail-field-label">商品类型</div><div class="detail-field-value">{$row.shop_type|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">商品标签</div><div class="detail-field-value">{$row.tag|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">商品标签(单选)</div><div class="detail-field-value">{$row.tag_backup|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">产地</div><div class="detail-field-value">{$row.from_area|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">仓库</div><div class="detail-field-value">{$row.store_city|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">商品标签 (输入)</div><div class="detail-field-value">{$row.tag_input|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">唯一id</div><div class="detail-field-value">{$row.uid|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">价格</div><div class="detail-field-value">{$row.price|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">详情</div><div class="detail-field-value">{$row.detail|default=''}</div></div>
</div>
</div>
<!-- 右侧基础信息 -->
<div class="layui-col-md4 detail-side">
<h3 class="detail-side-title">基础信息</h3>
<div class="detail-field-group">
<div class="detail-field-item">
<div class="detail-field-label">ID</div>
<div class="detail-field-value">{$row.id}</div>
</div>
<div class="detail-field-item"><div class="detail-field-label">排序</div><div class="detail-field-value">{$row.sort|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">状态</div><div class="detail-field-value">{$row.status|default=''}</div></div><div class="detail-field-item"><div class="detail-field-label">create_time</div><div class="detail-field-value">{$row.create_time|default=''}</div></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,22 @@
$(function(){
// 删除数据
window.deleteData = function(id) {
layer.confirm('确定要删除这条数据吗?', {
icon: 3,
title: '提示'
}, function(index) {
$.post('{{:url("delete")}}', {id: id}, function(res) {
if (res.code == 0) {
layer.msg('删除成功', {icon: 1}, function() {
location.href = '{{:url("index")}}';
});
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'json');
layer.close(index);
});
};
ua.listen();
})

View File

@@ -1454,6 +1454,129 @@ class BuildCurdServiceBase
);
$this->fileList[$viewEditFile] = $viewEditValue;
// 详情页面
$viewReadFile = "{$this->rootDir}app{$this->DS}admin{$this->DS}view{$this->DS}{$this->viewFilename}{$this->DS}read.html";
$mainFields = '';
$basicFields = '';
// 定义基础字段(右侧显示)
$basicFieldList = ['id', 'create_time', 'update_time', 'status', 'sort', 'delete_time'];
foreach ($this->tableColumns as $field => $val) {
if (in_array($field, ['id'])) {
continue;
}
$templateFile = "view{$this->DS}module{$this->DS}input";
$value = '{$row.' . $field . '|default=\'\'}';
// 根据字段类型选择不同的模板
if ($val['formType'] == 'image') {
$templateFile = "view{$this->DS}module{$this->DS}image";
} elseif ($val['formType'] == 'images') {
$templateFile = "view{$this->DS}module{$this->DS}images";
} elseif ($val['formType'] == 'file') {
$templateFile = "view{$this->DS}module{$this->DS}file";
} elseif ($val['formType'] == 'files') {
$templateFile = "view{$this->DS}module{$this->DS}files";
} elseif ($val['formType'] == 'editor' || in_array($field, ['remark']) || $val['formType'] == 'textarea') {
$templateFile = "view{$this->DS}module{$this->DS}textarea";
$value = '{$row.' . $field . '|raw|default=\'\'}';
} elseif ($val['formType'] == 'date') {
$templateFile = "view{$this->DS}module{$this->DS}date";
if (isset($val['define']) && !empty($val['define'])) {
$define = $val['define'];
} else {
$define = 'datetime';
}
if (!in_array($define, ['year', 'month', 'date', 'time', 'datetime'])) {
$define = 'datetime';
}
} elseif ($val['formType'] == 'radio' || $val['formType'] == 'switch') {
$templateFile = "view{$this->DS}module{$this->DS}radio";
if (isset($val['define']) && !empty($val['define'])) {
$define = $this->buildRadioView($field, '{in name="k" value="$row.' . $field . '"}checked=""{/in}');
}
} elseif ($val['formType'] == 'checkbox') {
$templateFile = "view{$this->DS}module{$this->DS}checkbox";
if (isset($val['define']) && !empty($val['define'])) {
$define = $this->buildCheckboxView($field, '{in name="k" value="$row.' . $field . '"}checked=""{/in}');
}
} elseif ($val['formType'] == 'select') {
$templateFile = "view{$this->DS}module{$this->DS}select";
if (isset($val['bindRelation'])) {
$define = $this->buildOptionView($val['bindRelation'], '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
} elseif (isset($val['define']) && !empty($val['define'])) {
$define = $this->buildOptionView($field, '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
}
} elseif ($val['formType'] == 'relation') {
$val['define']['type'] = 'radio';
$val['define']['valueField'] = 'id';
$val['define']['fieldName'] = $val['define']['relationBindSelect'];
$templateFile = "view{$this->DS}module{$this->DS}table";
$define = $this->buildTableView($field, $val, $value);
} elseif ($val['formType'] == 'table') {
$templateFile = "view{$this->DS}module{$this->DS}table";
$define = $this->buildTableView($field, $val, $value);
} elseif ($val['formType'] == 'city') {
$templateFile = "view{$this->DS}module{$this->DS}city";
$define = $this->buildCityView($field, $val, $value);
} elseif ($val['formType'] == 'tag') {
$templateFile = "view{$this->DS}module{$this->DS}tag";
}
// 直接生成详情页面的字段展示HTML使用简单的div结构
$fieldContent = '<div class="detail-field-item">';
$fieldContent .= '<div class="detail-field-label">' . $val['comment'] . '</div>';
$fieldContent .= '<div class="detail-field-value">';
// 根据字段类型处理值的显示
if ($val['formType'] == 'editor' || in_array($field, ['remark']) || $val['formType'] == 'textarea') {
$fieldContent .= '{$row.' . $field . '|raw|default=\'\'}';
} elseif ($val['formType'] == 'image') {
$fieldContent .= '<img src="{$row.' . $field . '}" class="detail-image" style="max-width: 200px; max-height: 200px;">';
} elseif ($val['formType'] == 'switch') {
$fieldContent .= '{eq name="row.' . $field . '" value="1"}启用{else/}禁用{/eq}';
} else {
$fieldContent .= '{$row.' . $field . '|default=\'\'}';
}
$fieldContent .= '</div>';
$fieldContent .= '</div>';
// 注释掉表单模板生成逻辑
/*
$fieldContent = $this->replaceTemplate(
$this->getTemplate($templateFile),
[
'comment' => $val['comment'],
'field' => $field,
'required' => '',
'required_text' => '',
'value' => $value,
'define' => $define,
]
);
*/
// 根据字段类型分配到不同区域
if (in_array($field, $basicFieldList)) {
$basicFields .= $fieldContent;
} else {
$mainFields .= $fieldContent;
}
}
$viewReadValue = $this->replaceTemplate(
$this->getTemplate("view{$this->DS}read"),
[
'mainFields' => $mainFields,
'basicFields' => $basicFields,
]
);
$this->fileList[$viewReadFile] = $viewReadValue;
return $this;
}
@@ -1538,7 +1661,8 @@ class BuildCurdServiceBase
}
}
$indexCols .= $this->formatColsRow("{width: 250, title: '操作', templet: ua.table.tool , fixed:'right'},\r");
$indexCols .= $this->formatColsRow("{width: 250, title: '操作', templet: ua.table.tool, operat: [{class: 'layui-btn layui-btn-primary layui-btn-xs', method: 'tab', field: 'id', text: '详情', title: '查看详情', auth: 'read', url: 'read', icon: ''}, 'edit', 'delete'], fixed:'right'},
");
$js_index = $this->replaceTemplate(
$this->getTemplate("js{$this->DS}index"),
@@ -1560,12 +1684,16 @@ class BuildCurdServiceBase
$js_add_file = "{$this->rootDir}app{$this->DS}admin{$this->DS}view{$this->DS}{$this->viewFilename}{$this->DS}add.js";
$js_edit_file = "{$this->rootDir}app{$this->DS}admin{$this->DS}view{$this->DS}{$this->viewFilename}{$this->DS}edit.js";
$js_read_file = "{$this->rootDir}app{$this->DS}admin{$this->DS}view{$this->DS}{$this->viewFilename}{$this->DS}read.js";
$js_add = $this->replaceTemplate($this->getTemplate("js{$this->DS}add"));
$this->fileList[$js_add_file] = $js_add;
$js_edit = $this->replaceTemplate($this->getTemplate("js{$this->DS}edit"));
$this->fileList[$js_edit_file] = $js_edit;
$js_read = $this->replaceTemplate($this->getTemplate("js{$this->DS}read"));
$this->fileList[$js_read_file] = $js_read;
return $this;
}

View File

@@ -0,0 +1,22 @@
$(function(){
// 删除数据
window.deleteData = function(id) {
layer.confirm('确定要删除这条数据吗?', {
icon: 3,
title: '提示'
}, function(index) {
$.post('{{:url("delete")}}', {id: id}, function(res) {
if (res.code == 0) {
layer.msg('删除成功', {icon: 1}, function() {
location.href = '{{:url("index")}}';
});
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'json');
layer.close(index);
});
};
ua.listen();
})

View File

@@ -0,0 +1,40 @@
<div class="layuimini-container detail-container">
<div class="layuimini-main">
<div class="layui-card detail-card">
<div class="layui-card-header detail-header">
<div class="layui-row">
<div class="layui-col-md9">
<h2 class="detail-title">#{$row.id} {$title}</h2>
<div class="detail-id">ID: {$row.id}</div>
</div>
<div class="layui-col-md3 text-right detail-actions">
<button class="layui-btn layui-btn-primary" onclick="window.history.back()">返回</button>
<button class="layui-btn" onclick="location.href='{{:url("edit", ["id" => $row.id])}}'">编辑</button>
<button class="layui-btn layui-btn-danger" onclick="deleteData({{$row.id}})">删除</button>
</div>
</div>
</div>
<div class="layui-card-body detail-content">
<div class="layui-row">
<!-- 左侧主体内容 -->
<div class="layui-col-md8 detail-main">
<div class="detail-field-group">
{{mainFields}}
</div>
</div>
<!-- 右侧基础信息 -->
<div class="layui-col-md4 detail-side">
<h3 class="detail-side-title">基础信息</h3>
<div class="detail-field-group">
<div class="detail-field-item">
<div class="detail-field-label">ID</div>
<div class="detail-field-value">{$row.id}</div>
</div>
{{basicFields}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -130,6 +130,23 @@ trait CurdTraitBase
return download($content, $export_file_name . date('YmdHis') . '.xlsx', true);
}
/**
* @\app\admin\service\annotation\NodeAnotation(title="详情")
*/
public function read($id)
{
$row = $this->model->find($id);
empty($row) && $this->error('数据不存在');
// 获取模型的标题(表注释)
$title = $row->title;
$this->assign('row', $row);
$this->assign('title', $title);
return $this->fetch();
}
/**
* @\app\admin\service\annotation\NodeAnotation(title="属性修改")
*/

View File

@@ -532,6 +532,130 @@ table样式
white-space: nowrap;
}
/**
详情页面样式
*/
/* 详情页面容器 */
.detail-container {
background-color: #ffffff;
}
/* 详情页面卡片 */
.detail-card {
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
/* 详情页面头部 */
.detail-header {
background-color: #f8f9fa;
border-bottom: 1px solid #e8e8e8;
}
/* 详情标题 */
.detail-title {
font-size: 20px;
font-weight: bold;
color: #333;
}
/* 详情ID */
.detail-id {
font-size: 12px;
color: #999;
}
/* 详情内容区域 */
.detail-content {
background-color: #ffffff;
}
/* 左侧主体内容 */
.detail-main {
background-color: #ffffff;
}
/* 右侧基础信息 */
.detail-side {
background-color: #f5f7fa;
border-radius: 4px;
}
/* 右侧面板标题 */
.detail-side-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #e8e8e8;
}
/* 字段组样式 */
.detail-field-group {
margin-bottom: 0;
}
/* 字段项样式 */
.detail-field-item {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
overflow: hidden;
}
.detail-field-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
/* 字段标签样式 */
.detail-field-label {
font-weight: bold;
color: #666;
margin-bottom: 5px;
font-size: 14px;
float: left;
width: 100px;
text-align: left;
}
/* 字段值样式 */
.detail-field-value {
color: #333;
line-height: 1.5;
font-size: 14px;
margin-left: 110px;
min-height: 30px;
word-break: break-word;
}
/* 详情图片样式 */
.detail-image {
max-width: 100%;
height: auto;
border-radius: 4px;
border: 1px solid #e8e8e8;
}
/* 详情操作按钮 */
.detail-actions {
text-align: right;
}
/* 响应式设计 */
@media (max-width: 768px) {
.detail-field-label {
float: none;
width: 100%;
margin-bottom: 5px;
}
.detail-field-value {
margin-left: 0;
}
}
.search-hide-item {
display: none;
}

View File

@@ -538,8 +538,131 @@ table样式
}
/**
详情页面样式
*/
/* 详情页面容器 */
.detail-container {
background-color: #ffffff;
}
/* 详情页面卡片 */
.detail-card {
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
/* 详情页面头部 */
.detail-header {
background-color: #f8f9fa;
border-bottom: 1px solid #e8e8e8;
}
/* 详情标题 */
.detail-title {
font-size: 20px;
font-weight: bold;
color: #333;
}
/* 详情ID */
.detail-id {
font-size: 12px;
color: #999;
}
/* 详情内容区域 */
.detail-content {
background-color: #ffffff;
}
/* 左侧主体内容 */
.detail-main {
background-color: #ffffff;
}
/* 右侧基础信息 */
.detail-side {
background-color: #f5f7fa;
border-radius: 4px;
}
/* 右侧面板标题 */
.detail-side-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #e8e8e8;
}
/* 字段组样式 */
.detail-field-group {
margin-bottom: 0;
}
/* 字段项样式 */
.detail-field-item {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
overflow: hidden;
}
.detail-field-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
/* 字段标签样式 */
.detail-field-label {
font-weight: bold;
color: #666;
margin-bottom: 5px;
font-size: 14px;
float: left;
width: 100px;
text-align: left;
}
/* 字段值样式 */
.detail-field-value {
color: #333;
line-height: 1.5;
font-size: 14px;
margin-left: 110px;
min-height: 30px;
word-break: break-word;
}
/* 详情图片样式 */
.detail-image {
max-width: 100%;
height: auto;
border-radius: 4px;
border: 1px solid #e8e8e8;
}
/* 详情操作按钮 */
.detail-actions {
text-align: right;
}
/* 响应式设计 */
@media (max-width: 768px) {
.detail-field-label {
float: none;
width: 100%;
margin-bottom: 5px;
}
.detail-field-value {
margin-left: 0;
}
}
.search-hide-item {
display: none;

View File

@@ -89,8 +89,7 @@
.elem-style-neomorphic .elem-style-sicfi .layui-side.layui-bg-black > .layuimini-menu-left > ul,
.elem-style-sicfi .elem-style-neomorphic .layuimini-menu-left-zoom > ul,
.elem-style-neomorphic .elem-style-sicfi .layuimini-menu-left-zoom > ul, .elem-style-sicfi .elem-style-neomorphic .layuimini-qiuck-module, .elem-style-neomorphic .elem-style-sicfi .layuimini-qiuck-module, .elem-style-sicfi .elem-style-neomorphic .panel, .elem-style-neomorphic .elem-style-sicfi .panel, .elem-style-sicfi .elem-style-neomorphic .layui-card, .elem-style-neomorphic .elem-style-sicfi .layui-card, .elem-style-sicfi .elem-style-neomorphic .layui-layer, .elem-style-neomorphic .elem-style-sicfi .layui-layer, .elem-style-sicfi .elem-style-neomorphic .layuimini-main, .elem-style-neomorphic .elem-style-sicfi .layuimini-main, .elem-style-sicfi .elem-style-neomorphic .layui-table-tool .layui-inline[lay-event], .elem-style-neomorphic .layui-table-tool .elem-style-sicfi .layui-inline[lay-event], .elem-style-sicfi .elem-style-win7 .layui-layer-easy .layui-layer-btn a, .elem-style-win7 .layui-layer-easy .layui-layer-btn .elem-style-sicfi a, .elem-style-sicfi .elem-style-nes .layui-layer-easy .layui-layer-btn a, .elem-style-nes .layui-layer-easy .layui-layer-btn .elem-style-sicfi a, .elem-style-sicfi .elem-style-nes .layuimini-container .layui-table-tool .layui-inline[lay-event], .elem-style-nes .layuimini-container .layui-table-tool .elem-style-sicfi .layui-inline[lay-event], .elem-style-sicfi .elem-style-gtk .layuimini-header-menu > li.layui-nav-item, .elem-style-gtk .elem-style-sicfi .layuimini-header-menu > li.layui-nav-item, .elem-style-sicfi .elem-style-gtk .layui-tab-brief > .layui-tab-title li, .elem-style-gtk .layui-tab-brief > .layui-tab-title .elem-style-sicfi li, .elem-style-sicfi .elem-style-gtk .layui-laydate-footer span, .elem-style-gtk .layui-laydate-footer .elem-style-sicfi span, .elem-style-sicfi .elem-style-gtk .laydate-footer-btns span, .elem-style-gtk .laydate-footer-btns .elem-style-sicfi span, .elem-style-sicfi .elem-style-gtk .layuimini-container .layui-table-tool .layui-inline[lay-event], .elem-style-gtk .layuimini-container .layui-table-tool .elem-style-sicfi .layui-inline[lay-event], .elem-style-sicfi .layui-layer-easy .layui-layer-btn a {
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
padding-right: 10px;
position: relative;
background-color: transparent;
@@ -119,8 +118,7 @@
display: block;
position: absolute;
z-index: -1;
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
width: 100%;
height: 100%;
left: -0.5px;
@@ -138,8 +136,7 @@
display: block;
position: absolute;
z-index: -1;
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
width: 100%;
height: 100%;
left: -0.5px;
@@ -275,8 +272,7 @@
border-color: rgb(126, 252, 246);
border-width: 1px;
border-style: solid;
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
position: absolute;
left: 0;
top: 0;
@@ -298,8 +294,7 @@
background-color: rgb(2, 17, 20) !important;
}
.elem-style-sicfi .layuimini-color .elem-content li {
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
}
.elem-style-sicfi .layuimini-color .elem-content li.layui-this {
background-color: rgb(126, 252, 246);
@@ -671,8 +666,7 @@
background-color: rgb(126, 252, 246);
border-color: rgb(6, 216, 215);
color: rgb(126, 252, 246) !important;
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
}
.elem-style-sicfi .layui-layer-shade {
background-color: #fff !important;
@@ -682,8 +676,7 @@
width: calc(100% - 10px);
}
.elem-style-sicfi .layuimini-menu-left .layui-nav .layui-nav-item {
-webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 0);
color: rgb(126, 252, 246);
background-color: rgba(62, 251, 251, 0.5);
}