完成开放平台配置页面,关闭eslint

master
李春波 2 years ago
parent 4d18d28632
commit 6b17bbf818
  1. 5
      .env.example
  2. 54
      app/admin/controller/PlatformController.php
  3. 29
      app/admin/model/Platform.php
  4. 86
      app/common/model/BaseModel.php
  5. 15
      app/common/model/Platform.php
  6. 5
      app/common/model/User.php
  7. 75
      config/plugin/tinywan/jwt/app.php
  8. 36
      config/thinkorm.php
  9. 5
      front/.eslintrc.js
  10. 40
      front/src/api/platform.js
  11. 3
      front/src/components/GlobalHeader/RightContent.vue
  12. 6
      front/src/components/Table/index.js
  13. 64
      front/src/config/router.config.js
  14. 14
      front/src/core/bootstrap.js
  15. 3
      front/src/permission.js
  16. 2
      front/src/store/modules/static-router.js
  17. 3
      front/src/store/modules/user.js
  18. 13
      front/src/utils/request.js
  19. 193
      front/src/views/platform/account.vue
  20. 82
      front/src/views/platform/modules/CreateForm.vue
  21. 2
      front/vue.config.js

@ -0,0 +1,5 @@
DB_HOST = 127.0.0.1
DB_PORT = 3306
DB_NAME =
DB_USER =
DB_PASSWORD =

@ -0,0 +1,54 @@
<?php
namespace app\admin\controller;
use app\admin\model\Platform;
use support\Request;
use support\Response;
use think\db\exception\DbException;
class PlatformController extends BaseController
{
public function add(Request $request)
{
$data = $request->post();
$res = Platform::create($data);
if ($res->id) {
return success();
} else {
return error('添加失败');
}
}
public function edit(Request $request)
{
$data = $request->post();
$res = Platform::update($data);
if ($res) {
return success();
} else {
return error('修改失败');
}
}
public function delete(Request $request)
{
$id = $request->post('id');
$res = Platform::destroy($id);
if ($res) {
return success();
} else {
return error();
}
}
/**
* @param Request $request
* @return array|Response
* @throws DbException
*/
public function list(Request $request)
{
return success(Platform::list($request));
}
}

@ -0,0 +1,29 @@
<?php
namespace app\admin\model;
use app\common\model\Platform as PlatformModel;
use support\Request;
use think\db\exception\DbException;
/**
* @property mixed $id
*/
class Platform extends PlatformModel
{
/**
* @param Request $request
* @return array
* @throws DbException
*/
public static function list(Request $request): array
{
$params = self::buildWhere($request);
return self::where($params['where'])
->order('id', 'desc')
->paginate([
'page' => $params['current_page'],
'list_rows' => $params['page_size']
])->toArray();
}
}

@ -2,9 +2,93 @@
namespace app\common\model;
use support\Request;
use think\Model;
class BaseModel extends Model
{
/**
* @param Request|null $request
* @param array $filter 导出时可能会用到,传数组
* @param array $pre 关联表需要指明要查的字段属于哪张表 ['erp_enquiry' => ['model', 'user_id', 'create_time']];
* @param callable|null $callback 自定义查询条件
* @return array
*/
protected static function buildWhere(Request $request = null, array $filter = [], array $pre = [], callable $callback = null): array
{
$column = array_column(static::$column, null, 'key');
if (!empty($request)) {
$current_page = $request->input('pageNo', 1);
$page_size = $request->input('pageSize', 10);
$params = $request->except(['token_', 'current', 'pageSize']);
} else {
$current_page = $filter['pageNo'] ?? 1;
$page_size = $filter['pageSize'] ?? 10;
$params = $filter;
}
$where = [];
foreach ($params as $key => $field) {
if (key_exists($key, $column) && !empty($field)) {
if (!is_array($field)) $field = trim($field);
$where_type = $column[$key]['where'] ?? '';
// 处理关联表
$keyArr = array_keys($pre);
foreach ($keyArr as $_key) {
if (in_array($key, $pre[$_key])) {
$key = $_key . '.' . $key;
break;
}
}
switch (trim($where_type)) {
case '>':
$where[] = [$key, '>', (int)$field];
break;
case '>=':
$where[] = [$key, '>=', $field];
break;
case '<':
$where[] = [$key, '<', (int)$field];
break;
case '<=':
$where[] = [$key, '<=', $field];
break;
case 'like%':
$where[] = [$key, 'like', "{$field}%"];
break;
case '%like':
$where[] = [$key, 'like', "%{$field}"];
break;
case 'like':
$where[] = [$key, 'like', "%{$field}%"];
break;
case 'time>':
$where[] = [$key, '>=', strtotime($field)];
break;
case 'in':
$field = str_replace(',', ',', $field);
$field = explode(',', $field);
$field = array_map('trim', $field);
$where[] = [$key, 'in', $field];
break;
case 'time_range':
$where[] = [$key, 'between', [strtotime(trim($field[0], '"')), strtotime(trim($field[1], '"'))]];
break;
case 'custom':
if (is_callable($callback)) {
if ($callback($key, $field) !== false) {
$where[] = $callback($key, $field);
}
}
break;
default:
$where[] = [$key, '=', $field];
}
}
}
return [
'page_size' => $page_size,
'current_page' => $current_page,
'where' => $where
];
}
}

@ -0,0 +1,15 @@
<?php
namespace app\common\model;
/**
* 开放平台参数
*/
class Platform extends BaseModel
{
static protected array $column = [
['key' => 'name', 'name' => '公司名称'],
['key' => 'app_id', 'name' => 'app_id'],
];
protected $table = 'platform';
}

@ -2,8 +2,9 @@
namespace app\common\model;
use app\common\model\BaseModel;
/**
* 用户
*/
class User extends BaseModel
{
protected $table = 'user';

@ -0,0 +1,75 @@
<?php
return [
'enable' => true,
'jwt' => [
/** 算法类型 HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、Ed25519 */
'algorithms' => 'HS256',
/** access令牌秘钥 */
'access_secret_key' => '2022d3d3LmJq',
/** access令牌过期时间,单位:秒。默认 2 小时 */
'access_exp' => 7200,
/** refresh令牌秘钥 */
'refresh_secret_key' => '2022KTxigxc9o50c',
/** refresh令牌过期时间,单位:秒。默认 7 天 */
'refresh_exp' => 604800,
/** refresh 令牌是否禁用,默认不禁用 false */
'refresh_disable' => false,
/** 令牌签发者 */
'iss' => 'webman.tinywan.cn',
/** 某个时间点后才能访问,单位秒。(如:30 表示当前时间30秒后才能使用) */
'nbf' => 0,
/** 时钟偏差冗余时间,单位秒。建议这个余地应该不大于几分钟 */
'leeway' => 60,
/** 是否允许单设备登录,默认不允许 false */
'is_single_device' => false,
/** 缓存令牌时间,单位:秒。默认 7 天 */
'cache_token_ttl' => 604800,
/** 缓存令牌前缀,默认 JWT:TOKEN: */
'cache_token_pre' => 'JWT:TOKEN:',
/** 用户信息模型 */
'user_model' => function ($uid) {
return [];
},
/** access令牌私钥 */
'access_private_key' => <<<EOD
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOD,
/** access令牌公钥 */
'access_public_key' => <<<EOD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOD,
/** refresh令牌私钥 */
'refresh_private_key' => <<<EOD
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOD,
/** refresh令牌公钥 */
'refresh_public_key' => <<<EOD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOD,
],
];

@ -0,0 +1,36 @@
<?php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => getenv('DB_HOST'),
// 数据库名
'database' => getenv('DB_NAME'),
// 数据库用户名
'username' => getenv('DB_USER'),
// 数据库密码
'password' => getenv('DB_PASSWORD'),
// 数据库连接端口
'hostport' => getenv('DB_PORT'),
// 数据库连接参数
'params' => [
// 连接超时3秒
\PDO::ATTR_TIMEOUT => 3,
],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => '',
// 断线重连
'break_reconnect' => true,
// 关闭SQL监听日志
'trigger_sql' => false,
// 自定义分页类
'bootstrap' => ''
],
],
];

@ -5,7 +5,7 @@ module.exports = {
},
'extends': [
'plugin:vue/strongly-recommended',
'@vue/standard'
// '@vue/standard'
],
rules: {
'no-console': 'off',
@ -56,7 +56,8 @@ module.exports = {
}
],
'template-curly-spacing': 'off',
'indent': 'off'
'indent': 'off',
'space-before-function-paren': 0
},
parserOptions: {
parser: 'babel-eslint'

@ -0,0 +1,40 @@
import request from '@/utils/request'
const api = {
Add: '/platform/add',
List: '/platform/list',
Edit: '/platform/edit',
Delete: '/platform/delete'
}
export function addPlatform(parameter) {
return request({
url: api.Add,
method: 'post',
data: parameter
})
}
export function getPlatform(parameter) {
return request({
url: api.List,
method: 'get',
params: parameter
})
}
export function editPlatform(parameter) {
return request({
url: api.Edit,
method: 'post',
data: parameter
})
}
export function deletePlatform(parameter) {
return request({
url: api.Delete,
method: 'post',
data: parameter
})
}

@ -8,6 +8,7 @@
<script>
import AvatarDropdown from './AvatarDropdown'
import SelectLang from '@/components/SelectLang'
import Store from '@/store'
export default {
name: 'RightContent',
@ -50,7 +51,7 @@ export default {
mounted () {
setTimeout(() => {
this.currentUser = {
name: 'Serati Ma'
name: Store.getters.nickname
}
}, 1500)
}

@ -165,8 +165,8 @@ export default {
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then(r => {
this.localPagination = this.showPagination && Object.assign({}, this.localPagination, {
current: r.pageNo, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
current: r.current_page, // 返回结果中的当前分页数
total: r.total, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
@ -181,7 +181,7 @@ export default {
// 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) {
if ((['auto', true].includes(this.showPagination) && r.total <= (r.current_page * this.localPagination.pageSize))) {
this.localPagination.hideOnSinglePage = true
}
} catch (e) {

@ -1,5 +1,5 @@
// eslint-disable-next-line
import { UserLayout, BasicLayout, BlankLayout } from '@/layouts'
import {BasicLayout, UserLayout} from '@/layouts'
// eslint-disable-next-line
import {bxAnaalyse} from '@/core/icons'
@ -70,6 +70,68 @@ export const asyncRouterMap = [
meta: { title: 'menu.form.advanced-form', keepAlive: true }
}
]
},
{
path: '/list',
name: 'list',
component: RouteView,
redirect: '/list/table-list',
meta: {title: 'menu.list', icon: 'table'},
children: [
{
path: '/list/table-list/:pageNo([1-9]\\d*)?',
name: 'TableListWrapper',
hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
component: () => import('@/views/list/TableList'),
meta: {title: 'menu.list.table-list', keepAlive: true}
},
{
path: '/list/basic-list',
name: 'BasicList',
component: () => import('@/views/list/BasicList'),
meta: {title: 'menu.list.basic-list', keepAlive: true}
},
{
path: '/list/card',
name: 'CardList',
component: () => import('@/views/list/CardList'),
meta: {title: 'menu.list.card-list', keepAlive: true}
},
{
path: '/list/search',
name: 'SearchList',
component: () => import('@/views/list/search/SearchLayout'),
redirect: '/list/search/article',
meta: {title: 'menu.list.search-list', keepAlive: true},
children: [
{
path: '/list/search/article',
name: 'SearchArticles',
component: () => import('../views/list/search/Article'),
meta: {title: 'menu.list.search-list.articles'}
},
{
path: '/list/search/project',
name: 'SearchProjects',
component: () => import('../views/list/search/Projects'),
meta: {title: 'menu.list.search-list.projects'}
},
{
path: '/list/search/application',
name: 'SearchApplications',
component: () => import('../views/list/search/Applications'),
meta: {title: 'menu.list.search-list.applications'}
}
]
}
]
},
// platform
{
name: 'platform',
path: '/platform',
component: () => import('@/views/platform/account'),
meta: {title: '开放平台', keepAlive: true, icon: bxAnaalyse}
}
]
},

@ -3,17 +3,21 @@ import storage from 'store'
import {
ACCESS_TOKEN,
APP_LANGUAGE,
TOGGLE_COLOR,
TOGGLE_CONTENT_WIDTH,
TOGGLE_FIXED_HEADER,
TOGGLE_FIXED_SIDEBAR, TOGGLE_HIDE_HEADER,
TOGGLE_LAYOUT, TOGGLE_NAV_THEME, TOGGLE_WEAK,
TOGGLE_COLOR, TOGGLE_MULTI_TAB
TOGGLE_FIXED_SIDEBAR,
TOGGLE_HIDE_HEADER,
TOGGLE_LAYOUT,
TOGGLE_MULTI_TAB,
TOGGLE_NAV_THEME,
TOGGLE_WEAK
} from '@/store/mutation-types'
import { printANSI } from '@/utils/screenLog'
// import { printANSI } from '@/utils/screenLog'
import defaultSettings from '@/config/defaultSettings'
export default function Initializer () {
printANSI() // 请自行移除该行. please remove this line
// printANSI() // 请自行移除该行. please remove this line
store.commit(TOGGLE_LAYOUT, storage.get(TOGGLE_LAYOUT, defaultSettings.layout))
store.commit(TOGGLE_FIXED_HEADER, storage.get(TOGGLE_FIXED_HEADER, defaultSettings.fixedHeader))

@ -4,7 +4,7 @@ import storage from 'store'
import NProgress from 'nprogress' // progress bar
import '@/components/NProgress/nprogress.less' // progress bar custom style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import {domTitle, setDocumentTitle} from '@/utils/domUtil'
import {ACCESS_TOKEN} from '@/store/mutation-types'
import {i18nRender} from '@/locales'
@ -30,7 +30,6 @@ router.beforeEach((to, from, next) => {
store
.dispatch('GetInfo')
.then(res => {
console.log('res', res)
// 根据用户权限信息生成可访问的路由表
store.dispatch('GenerateRoutes', { token, ...res }).then(() => {
// 动态添加可访问路由表

@ -10,7 +10,7 @@ import cloneDeep from 'lodash.clonedeep'
*/
function hasPermission (permission, route) {
if (route.meta && route.meta.permission) {
console.log('hasPermission', permission)
// console.log('hasPermission', permission)
if (permission === undefined) {
return false
}

@ -1,6 +1,6 @@
import storage from 'store'
import expirePlugin from 'store/plugins/expire'
import { login, getInfo, logout } from '@/api/login'
import {getInfo, login, logout} from '@/api/login'
import {ACCESS_TOKEN} from '@/store/mutation-types'
import {welcome} from '@/utils/util'
@ -56,7 +56,6 @@ const user = {
// 请求后端获取用户信息 /api/user/info
getInfo().then(response => {
const result = response.data
console.log(result)
if (result.role && result.role.permissions.length > 0) {
const role = { ...result.role }
role.permissions = result.role.permissions.map(permission => {

@ -2,6 +2,7 @@ import axios from 'axios'
import store from '@/store'
import storage from 'store'
import notification from 'ant-design-vue/es/notification'
import message from 'ant-design-vue/es/message'
import {VueAxios} from './axios'
import {ACCESS_TOKEN} from '@/store/mutation-types'
@ -23,8 +24,12 @@ const errorHandler = (error) => {
message: 'Forbidden',
description: data.message
})
}
if (error.response.status === 401 && !(data.result && data.result.isLogin)) {
} else if (error.response.status === 404) {
notification.error({
message: '404',
description: '请求地址不存在'
})
} else if (error.response.status === 401 && !(data.result && data.result.isLogin)) {
notification.error({
message: 'Unauthorized',
description: 'Authorization verification failed'
@ -54,6 +59,10 @@ request.interceptors.request.use(config => {
// response interceptor
request.interceptors.response.use((response) => {
if (response.data.code !== 0) {
message.error(response.data.msg || '请求出错')
return Promise.reject(response.data.msg)
}
return response.data
}, errorHandler)

@ -0,0 +1,193 @@
<template>
<page-header-wrapper :title="false" content="开放平台参数配置">
<a-card :bordered="false">
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="48">
<a-col :md="8" :sm="24">
<a-form-item label="名称">
<a-input v-model="queryParam.name" placeholder=""/>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24">
<a-form-item label="app_id">
<a-input v-model="queryParam.app_id" placeholder=""/>
</a-form-item>
</a-col>
<span :style="advanced && { float: 'right', overflow: 'hidden' } || {} "
class="table-page-search-submitButtons">
<a-button type="primary" @click="$refs.table.refresh(true)">查询</a-button>
<a-button style="margin-left: 8px" @click="() => this.queryParam = {}">重置</a-button>
</span>
</a-row>
</a-form>
</div>
<div class="table-operator">
<a-button icon="plus" type="primary" @click="handleAdd">新建</a-button>
</div>
<s-table
ref="table"
:columns="columns"
:data="loadData"
rowKey="id"
showPagination="auto"
size="default"
>
<span slot="action" slot-scope="text, record">
<template>
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical"/>
<a-popconfirm title="确定删除该记录吗?" @confirm="handleDel(record.id)">
<a>删除</a>
</a-popconfirm>
</template>
</span>
</s-table>
<create-form
ref="createModal"
:loading="confirmLoading"
:model="mdl"
:visible="visible"
@cancel="handleCancel"
@ok="handleOk"
/>
</a-card>
</page-header-wrapper>
</template>
<script>
import {Ellipsis, STable} from '@/components'
import {addPlatform, deletePlatform, editPlatform, getPlatform} from '@/api/platform'
import CreateForm from './modules/CreateForm'
const columns = [
{
title: '名称',
dataIndex: 'name'
},
{
title: 'app_id',
dataIndex: 'app_id'
},
{
title: 'secret',
dataIndex: 'secret'
},
{
title: 'token',
dataIndex: 'token'
},
{
title: 'aes_key',
dataIndex: 'aes_key'
},
{
title: '更新时间',
dataIndex: 'update_time',
sorter: true
},
{
title: '操作',
dataIndex: 'action',
width: '150px',
scopedSlots: {customRender: 'action'}
}
]
export default {
name: 'account',
components: {
STable,
Ellipsis,
CreateForm
},
data() {
this.columns = columns
return {
// create model
visible: false,
confirmLoading: false,
mdl: null,
// /
advanced: false,
//
queryParam: {},
// Promise
loadData: parameter => {
const requestParameters = Object.assign({}, parameter, this.queryParam)
return getPlatform(requestParameters)
.then(res => {
return res.data
})
},
selectedRowKeys: [],
selectedRows: []
}
},
created() {
// getRoleList({ t: new Date() })
},
computed: {},
methods: {
handleAdd() {
this.mdl = null
this.visible = true
},
handleEdit(record) {
this.visible = true
this.mdl = {...record}
},
handleOk() {
const form = this.$refs.createModal.form
this.confirmLoading = true
form.validateFields((errors, values) => {
if (!errors) {
console.log('values', values)
if (values.id > 0) {
editPlatform(values).then(res => {
this.visible = false
form.resetFields()
this.$refs.table.refresh()
this.$message.info(res['msg'])
}).catch(e => {
console.log(e)
}).finally(() => {
this.confirmLoading = false
})
} else {
addPlatform(values).then(res => {
this.visible = false
form.resetFields()
this.$refs.table.refresh()
this.$message.info(res['msg'])
}).catch(e => {
console.log(e)
}).finally(() => {
this.confirmLoading = false
})
}
} else {
this.confirmLoading = false
}
})
},
handleCancel() {
this.visible = false
const form = this.$refs.createModal.form
form.resetFields() //
},
handleDel(id) {
deletePlatform({id}).then(res => {
//
this.$refs.table.refresh()
this.$message.info(res['msg'])
}).catch(e => {
console.log(e)
})
}
}
}
</script>

@ -0,0 +1,82 @@
<template>
<a-modal
:confirmLoading="loading"
:visible="visible"
:width="640"
title="新建"
@cancel="() => { $emit('cancel') }"
@ok="() => { $emit('ok') }"
>
<a-spin :spinning="loading">
<a-form :form="form" v-bind="formLayout">
<!-- 检查是否有 id 并且大于0大于0是修改其他是新增新增不显示主键ID -->
<a-form-item v-show="model && model.id > 0" label="主键ID">
<a-input v-decorator="['id', { initialValue: 0 }]" disabled/>
</a-form-item>
<a-form-item label="名称">
<a-input v-decorator="['name', {rules: [{required: true, message: '请输入名称'}]}]"/>
</a-form-item>
<a-form-item label="app_id">
<a-input v-decorator="['app_id', {rules: [{required: true, message: '请输入app_id'}]}]"/>
</a-form-item>
<a-form-item label="secret">
<a-input v-decorator="['secret', {rules: [{required: true, message: '请输入secret'}]}]"/>
</a-form-item>
<a-form-item label="token">
<a-input v-decorator="['token', {rules: [{required: true, message: '请输入token'}]}]"/>
</a-form-item>
<a-form-item label="aes_key">
<a-input v-decorator="['aes_key', {rules: [{required: true, message: '请输入aes_key'}]}]"/>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import pick from 'lodash.pick'
//
const fields = ['id', 'name', 'app_id', 'secret', 'token', 'aes_key']
export default {
props: {
visible: {
type: Boolean,
required: true
},
loading: {
type: Boolean,
default: () => false
},
model: {
type: Object,
default: () => null
}
},
data() {
this.formLayout = {
labelCol: {
xs: {span: 24},
sm: {span: 7}
},
wrapperCol: {
xs: {span: 24},
sm: {span: 13}
}
}
return {
form: this.$form.createForm(this)
}
},
created() {
//
fields.forEach(v => this.form.getFieldDecorator(v))
// model
this.$watch('model', () => {
this.model && this.form.setFieldsValue(pick(this.model, fields))
})
}
}
</script>

@ -136,7 +136,7 @@ const vueConfig = {
// disable source map in production
productionSourceMap: false,
lintOnSave: undefined,
lintOnSave: false,
// babel-loader no-ignore node_modules/*
transpileDependencies: []
}

Loading…
Cancel
Save