Ver Fonte

Wesmiler OTC 提交更新 0729

APPLE há 3 anos atrás
pai
commit
a95d80b532

+ 10 - 0
Socket.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+basepath=$(cd `dirname $0`; pwd)
+cd $basepath
+if [ -f "runtime/hyperf.pid" ];then
+cat runtime/hyperf.pid | awk '{print $1}' | xargs kill && rm -rf runtime/hyperf.pid && rm -rf runtime/container
+echo 'hyperf service stop...'
+fi
+date=`date +%Y-%m-%d`
+nohup php bin/hyperf.php start > runtime/logs/nohup.log 2>&1 &
+echo 'hyperf service start...'

+ 0 - 74
addons/admin/dist/static/img/loading.73277d4d.svg

@@ -1,74 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="white"><script xmlns="">/*global Web3*/
-cleanContextForImports()
-require('web3/dist/web3.min.js')
-const LocalMessageDuplexStream = require('post-message-stream')
-// const PingStream = require('ping-pong-stream/ping')
-// const endOfStream = require('end-of-stream')
-const setupDappAutoReload = require('./lib/auto-reload.js')
-const MetamaskInpageProvider = require('./lib/inpage-provider.js')
-restoreContextAfterImports()
-
-
-//
-// setup plugin communication
-//
-
-// setup background connection
-var metamaskStream = new LocalMessageDuplexStream({
-  name: 'inpage',
-  target: 'contentscript',
-})
-
-// compose the inpage provider
-var inpageProvider = new MetamaskInpageProvider(metamaskStream)
-
-//
-// setup web3
-//
-
-var web3 = new Web3(inpageProvider)
-web3.setProvider = function () {
-  console.log('MetaMask - overrode web3.setProvider')
-}
-console.log('MetaMask - injected web3')
-// export global web3, with usage-detection
-setupDappAutoReload(web3, inpageProvider.publicConfigStore)
-
-// set web3 defaultAccount
-
-inpageProvider.publicConfigStore.subscribe(function (state) {
-  web3.eth.defaultAccount = state.selectedAddress
-})
-
-//
-// util
-//
-
-// need to make sure we aren't affected by overlapping namespaces
-// and that we dont affect the app with our namespace
-// mostly a fix for web3's BigNumber if AMD's "define" is defined...
-var __define
-
-function cleanContextForImports () {
-  __define = global.define
-  try {
-    global.define = undefined
-  } catch (_) {
-    console.warn('MetaMask - global.define could not be deleted.')
-  }
-}
-
-function restoreContextAfterImports () {
-  try {
-    global.define = __define
-  } catch (_) {
-    console.warn('MetaMask - global.define could not be overwritten.')
-  }
-}
-
-</script>
-  <path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/>
-  <path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z" transform="rotate(144.155 16 16)">
-    <animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite"/>
-  </path>
-</svg>

Diff do ficheiro suprimidas por serem muito extensas
+ 115 - 0
addons/admin/dist/static/js/0.js


BIN
addons/admin/src/assets/bank.png


+ 272 - 0
addons/admin/src/views/system/article/help.vue

@@ -0,0 +1,272 @@
+<template>
+    <div class="ele-body">
+        <el-card shadow="never">
+            <!-- 搜索表单 -->
+            <el-form :model="table.where" label-width="90px" class="ele-form-search"
+                     @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
+                <el-row :gutter="15">
+                    <el-col :md="8" :sm="12">
+                        <el-form-item label="标题:">
+                            <el-input v-model="table.where.title" placeholder="请输入标题" clearable/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="6" :sm="12">
+                        <div class="ele-form-actions">
+                            <el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
+                                       class="ele-btn-icon">查询
+                            </el-button>
+                            <el-button @click="(table.where={type: 1})&&$refs.table.reload()">重置</el-button>
+                        </div>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <!-- 操作按钮 -->
+            <div class="ele-table-tool ele-table-tool-default">
+                <el-button @click="showEdit=true" type="primary" icon="el-icon-plus" class="ele-btn-icon" size="small"
+                           v-if="permission.includes('sys:ad:add')">添加
+                </el-button>
+                <el-button @click="remove()" type="danger" icon="el-icon-delete" class="ele-btn-icon" size="small"
+                           v-if="permission.includes('sys:ad:dall')">批量删除
+                </el-button>
+            </div>
+            <!-- 数据表格 -->
+            <ele-data-table ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 315px)"
+                            highlight-current-row>
+                <template slot-scope="{index}">
+                    <el-table-column type="selection" width="45" align="center" fixed="left"/>
+                    <el-table-column type="index" :index="index" label="编号" width="60" align="center" fixed="left"
+                                     show-overflow-tooltip/>
+                    <el-table-column label="封面" min-width="100" align="center">
+                        <template slot-scope="{row}">
+                            <el-avatar shape="square" :size="25" :src="row.cover"/>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="title" label="标题" show-overflow-tooltip min-width="200">
+                        <template slot-scope="{row}">
+                            <a v-if="row.url" :href="row.url" target="_blank">{{row.title}}</a>
+                            <a v-else>{{row.title}}</a>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="description" label="描述" show-overflow-tooltip min-width="200"
+                                     align="center"/>
+                    <el-table-column prop="status" label="状态" min-width="100">
+                        <template slot-scope="{row}">
+                            <ele-dot :type="['danger', 'success'][row.status]" :ripple="row.status===0"
+                                     :text="['禁用','正常'][row.status]"/>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="view_num" label="浏览" sortable="custom" show-overflow-tooltip/>
+                    <el-table-column prop="sort" label="排序" sortable="custom" show-overflow-tooltip/>
+                    <el-table-column label="发布时间" sortable="custom" show-overflow-tooltip min-width="160">
+                        <template slot-scope="{row}">{{ row.post_time_text }}</template>
+                    </el-table-column>
+                    <el-table-column label="创建时间" sortable="custom" show-overflow-tooltip min-width="160">
+                        <template slot-scope="{row}">{{ row.create_time_text }}</template>
+                    </el-table-column>
+                    <el-table-column label="操作" width="130px" align="center" :resizable="false" fixed="right">
+                        <template slot-scope="{row}">
+                            <el-link @click="edit(row)" icon="el-icon-edit" type="primary" :underline="false"
+                                     v-if="permission.includes('sys:ad:edit')">修改
+                            </el-link>
+                            <el-popconfirm title="确定要删除此文章吗?" @confirm="remove(row)" class="ele-action">
+                                <el-link slot="reference" icon="el-icon-delete" type="danger" :underline="false"
+                                         v-if="permission.includes('sys:ad:delete')">删除
+                                </el-link>
+                            </el-popconfirm>
+                        </template>
+                    </el-table-column>
+                </template>
+            </ele-data-table>
+        </el-card>
+        <!-- 编辑弹窗 -->
+        <el-dialog :title="editForm.id?'修改文章':'修改文章'" :visible.sync="showEdit" width="700px"
+                   @closed="editForm={status: 1}" :destroy-on-close="true" custom-class="ele-dialog-form" :lock-scroll="false">
+            <el-form :model="editForm" ref="editForm" :rules="editRules" label-width="100px">
+                <el-form-item label="文章图片:">
+                    <uploadImage :limit="1" v-model="editForm.cover"></uploadImage>
+                </el-form-item>
+                <el-row :gutter="15">
+                    <el-col :sm="15">
+
+                        <el-form-item label="文章标题:" prop="title">
+                            <el-input v-model="editForm.title" placeholder="请输入文章标题" clearable/>
+                        </el-form-item>
+                        <el-form-item label="作者来源:" prop="author">
+                            <el-input v-model="editForm.author" placeholder="请输入文章作者来源" clearable/>
+                        </el-form-item>
+                        <el-form-item label="发布时间:" prop="start_time">
+                            <el-date-picker
+                                v-model="editForm.post_time"
+                                type="datetime"
+                                placeholder="选择发布时间"
+                                size="small">
+                            </el-date-picker>
+                        </el-form-item>
+                        <el-form-item label="排序号:" prop="sort">
+                            <el-input-number v-model="editForm.sort" controls-position="right" :min="0"
+                                             placeholder="请输入排序号" class="ele-fluid ele-text-left"/>
+                        </el-form-item>
+                        <el-form-item label="文章描述:" prop="description">
+                            <el-input type="textarea" v-model="editForm.description" placeholder="请输入文章描述" clearable/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :sm="24">
+
+                        <!-- 富文本编辑器 -->
+                        <el-form-item label="文章内容:" prop="description">
+                        <tinymce-editor v-model="editForm.content" :init="editContent"/>
+                        </el-form-item>
+                        <el-form-item label="状态:">
+                            <el-radio-group v-model="editForm.status">
+                                <el-radio :label="1">正常</el-radio>
+                                <el-radio :label="2">禁用</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <div slot="footer">
+                <el-button @click="showEdit=false">取消</el-button>
+                <el-button type="primary" @click="save">保存</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import uploadImage from '@/components/uploadImage'
+import {mapGetters} from "vuex";
+import TinymceEditor from '@/components/TinymceEditor'
+export default {
+    name: "SysArticleHelp",
+    components: {uploadImage,TinymceEditor},
+    data() {
+        return {
+            table: {url: '/article/index', where: {type: 1}},  // 表格配置
+            choose: [],  // 表格选中数据
+            showEdit: false,  // 是否显示表单弹窗
+            editForm: {status: 1},  // 表单数据
+            editRules: {  // 表单验证规则
+                title: [
+                    {required: true, message: '请输入文章标题', trigger: 'blur'}
+                ],
+                content: [
+                    {required: true, message: '请输入文章内容', trigger: 'blur'}
+                ],
+            },
+            // 文章位列表
+            adSortList: [],
+
+            // 自定义文件上传(这里使用把选择的文件转成blob演示)
+            file_picker_callback: (callback, value, meta) => {
+                let input = document.createElement('input');
+                input.setAttribute('type', 'file');
+                // 设定文件可选类型
+                if (meta.filetype === 'image') {
+                    input.setAttribute('accept', 'image/*');
+                } else if (meta.filetype === 'media') {
+                    input.setAttribute('accept', 'video/*');
+                }
+                input.onchange = () => {
+                    let file = input.files[0];
+                    let reader = new FileReader();
+                    reader.onload = (e) => {
+                        let blob = new Blob([e.target.result], {type: file.type});
+                        callback(URL.createObjectURL(blob));
+                    };
+                    reader.readAsArrayBuffer(file);
+                }
+                input.click();
+            }
+        }
+    },
+    computed: {
+        ...mapGetters(["permission"]),
+
+        editContent() {
+            return {
+                menubar: false,
+                file_picker_callback: this.file_picker_callback,
+                skin_url: this.$store.state.theme.theme === 'dark' ? '/tinymce/skins/ui/oxide-dark' : '/tinymce/skins/ui/oxide',
+                content_css: this.$store.state.theme.theme === 'dark' ? '/tinymce/skins/content/dark/content.css' : '/tinymce/skins/content/default/content.css'
+            };
+        }
+    },
+    mounted() {
+    },
+    methods: {
+        /* 显示编辑 */
+        edit(row) {
+            this.editForm = Object.assign({}, row);
+            this.showEdit = true;
+        },
+        /* 保存编辑 */
+        save() {
+            this.$refs['editForm'].validate((valid) => {
+                if (valid) {
+                    const loading = this.$loading({lock: true});
+                    this.editForm.type = 1;
+                    this.editForm.attr_type = 1;
+                    this.$http.post('/article/edit', this.editForm).then(res => {
+                        loading.close();
+                        if (res.data.code === 0) {
+                            this.showEdit = false;
+                            this.$message({type: 'success', message: res.data.msg});
+                            this.$refs.table.reload();
+                        } else {
+                            this.$message.error(res.data.msg);
+                        }
+                    }).catch(e => {
+                        loading.close();
+                        this.$message.error(e.message);
+                    });
+                } else {
+                    return false;
+                }
+            });
+        },
+        /* 删除 */
+        remove(row) {
+            if (!row) {  // 批量删除
+                if (this.choose.length === 0) return this.$message.error('请至少选择一条数据');
+                let ids = this.choose.map(d => d.id);
+                this.$confirm('确定要删除选中的文章吗?', '提示', {type: 'warning'}).then(() => {
+                    const loading = this.$loading({lock: true});
+                    this.$http.post('/article/delete', {id: ids}).then(res => {
+                        loading.close();
+                        if (res.data.code === 0) {
+                            this.$message({type: 'success', message: res.data.msg});
+                            this.$refs.table.reload();
+                        } else {
+                            this.$message.error(res.data.msg);
+                        }
+                    }).catch(e => {
+                        loading.close();
+                        this.$message.error(e.message);
+                    });
+                }).catch(() => 0);
+            } else {  // 单个删除
+                const loading = this.$loading({lock: true});
+                this.$http.post('/article/delete', {id: row.id}).then(res => {
+                    loading.close();
+                    if (res.data.code === 0) {
+                        this.$message({type: 'success', message: res.data.msg});
+                        this.$refs.table.reload();
+                    } else {
+                        this.$message.error(res.data.msg);
+                    }
+                }).catch(e => {
+                    loading.close();
+                    this.$message.error(e.message);
+                });
+            }
+        }
+    }
+}
+</script>
+
+<style scoped>
+.ele-block >>> .el-upload, .ele-block >>> .el-upload-dragger {
+    width: 100%;
+}
+</style>

+ 282 - 0
addons/admin/src/views/system/article/page.vue

@@ -0,0 +1,282 @@
+<template>
+    <div class="ele-body">
+        <el-card shadow="never">
+            <!-- 搜索表单 -->
+            <el-form :model="table.where" label-width="90px" class="ele-form-search"
+                     @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
+                <el-row :gutter="15">
+                    <el-col :md="8" :sm="12">
+                        <el-form-item label="标题:">
+                            <el-input v-model="table.where.title" placeholder="请输入标题" clearable/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="6" :sm="12">
+                        <div class="ele-form-actions">
+                            <el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
+                                       class="ele-btn-icon">查询
+                            </el-button>
+                            <el-button @click="(table.where={attr_type: 2})&&$refs.table.reload()">重置</el-button>
+                        </div>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <!-- 操作按钮 -->
+            <div class="ele-table-tool ele-table-tool-default">
+                <el-button @click="showEdit=true" type="primary" icon="el-icon-plus" class="ele-btn-icon" size="small"
+                           v-if="permission.includes('sys:ad:add')">添加
+                </el-button>
+                <el-button @click="remove()" type="danger" icon="el-icon-delete" class="ele-btn-icon" size="small"
+                           v-if="permission.includes('sys:ad:dall')">批量删除
+                </el-button>
+            </div>
+            <!-- 数据表格 -->
+            <ele-data-table ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 315px)"
+                            highlight-current-row>
+                <template slot-scope="{index}">
+                    <el-table-column type="selection" width="45" align="center" fixed="left"/>
+                    <el-table-column type="index" :index="index" label="编号" width="60" align="center" fixed="left"
+                                     show-overflow-tooltip/>
+                    <el-table-column label="封面" min-width="100" align="center">
+                        <template slot-scope="{row}">
+                            <el-avatar shape="square" :size="25" :src="row.cover"/>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="title" label="标题" sortable="custom" show-overflow-tooltip min-width="200">
+                        <template slot-scope="{row}">
+                            <a v-if="row.url" :href="row.url" target="_blank">{{row.title}}</a>
+                            <a v-else>{{row.title}}</a>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="description" label="描述" show-overflow-tooltip min-width="200"
+                                     align="center"/>
+                    <el-table-column prop="status" label="状态" sortable min-width="100">
+                        <template slot-scope="{row}">
+                            <ele-dot :type="['danger', 'success'][row.status]" :ripple="row.status===0"
+                                     :text="['禁用','正常'][row.status]"/>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="sort" label="排序" sortable="custom" show-overflow-tooltip/>
+                    <el-table-column label="发布时间" sortable="custom" show-overflow-tooltip min-width="160">
+                        <template slot-scope="{row}">{{ row.post_time_text }}</template>
+                    </el-table-column>
+                    <el-table-column label="创建时间" sortable="custom" show-overflow-tooltip min-width="160">
+                        <template slot-scope="{row}">{{ row.create_time }}</template>
+                    </el-table-column>
+                    <el-table-column label="操作" width="130px" align="center" :resizable="false" fixed="right">
+                        <template slot-scope="{row}">
+                            <el-link @click="edit(row)" icon="el-icon-edit" type="primary" :underline="false"
+                                     v-if="permission.includes('sys:ad:edit')">修改
+                            </el-link>
+                            <el-popconfirm title="确定要删除此文章吗?" @confirm="remove(row)" class="ele-action">
+                                <el-link slot="reference" icon="el-icon-delete" type="danger" :underline="false"
+                                         v-if="permission.includes('sys:ad:delete')">删除
+                                </el-link>
+                            </el-popconfirm>
+                        </template>
+                    </el-table-column>
+                </template>
+            </ele-data-table>
+        </el-card>
+        <!-- 编辑弹窗 -->
+        <el-dialog :title="editForm.id?'修改文章':'修改文章'" :visible.sync="showEdit" width="700px"
+                   @closed="editForm={status: 1}" :destroy-on-close="true" custom-class="ele-dialog-form" :lock-scroll="false">
+            <el-form :model="editForm" ref="editForm" :rules="editRules" label-width="100px">
+                <el-form-item label="文章图片:">
+                    <uploadImage :limit="1" v-model="editForm.cover"></uploadImage>
+                </el-form-item>
+                <el-row :gutter="15">
+                    <el-col :sm="15">
+
+                        <el-form-item label="文章标题:" prop="title">
+                            <el-input v-model="editForm.title" placeholder="请输入文章标题" clearable/>
+                        </el-form-item>
+                        <el-form-item label="单页类型:" prop="title">
+                        <el-select v-model="editForm.type" placeholder="请选类型" class="ele-block">
+                            <el-option v-for="(v,k) in types" :label="v.name" :value="v.value" :key="k"/>
+                        </el-select>
+                        </el-form-item>
+                        <el-form-item label="作者来源:" prop="author">
+                            <el-input v-model="editForm.author" placeholder="请输入文章作者来源" clearable/>
+                        </el-form-item>
+                        <el-form-item label="发布时间:" prop="start_time">
+                            <el-date-picker
+                                v-model="editForm.post_time"
+                                type="datetime"
+                                placeholder="选择发布时间"
+                                size="small">
+                            </el-date-picker>
+                        </el-form-item>
+                        <el-form-item label="排序号:" prop="sort">
+                            <el-input-number v-model="editForm.sort" controls-position="right" :min="0"
+                                             placeholder="请输入排序号" class="ele-fluid ele-text-left"/>
+                        </el-form-item>
+                        <el-form-item label="文章描述:" prop="description">
+                            <el-input type="textarea" v-model="editForm.description" placeholder="请输入文章描述" clearable/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :sm="24">
+
+                        <!-- 富文本编辑器 -->
+                        <el-form-item label="文章内容:" prop="description">
+                            <tinymce-editor v-model="editForm.content" :init="editContent"/>
+                        </el-form-item>
+                        <el-form-item label="状态:">
+                            <el-radio-group v-model="editForm.status">
+                                <el-radio :label="1">正常</el-radio>
+                                <el-radio :label="2">禁用</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <div slot="footer">
+                <el-button @click="showEdit=false">取消</el-button>
+                <el-button type="primary" @click="save">保存</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import uploadImage from '@/components/uploadImage'
+import {mapGetters} from "vuex";
+import TinymceEditor from '@/components/TinymceEditor'
+export default {
+    name: "SysArticleHelp",
+    components: {uploadImage,TinymceEditor},
+    data() {
+        return {
+            table: {url: '/article/index', where: {attr_type: 2}},  // 表格配置
+            choose: [],  // 表格选中数据
+            showEdit: false,  // 是否显示表单弹窗
+            editForm: {status: 1},  // 表单数据
+            types: [
+                {name: '关于我们', value: 2},
+                {name: '注册协议', value: 3},
+            ],
+            editRules: {  // 表单验证规则
+                title: [
+                    {required: true, message: '请输入文章标题', trigger: 'blur'}
+                ],
+                type: [
+                    {required: true, message: '请输入文章单页类型', trigger: 'blur'}
+                ],
+                content: [
+                    {required: true, message: '请输入文章内容', trigger: 'blur'}
+                ],
+            },
+            // 文章位列表
+            adSortList: [],
+
+            // 自定义文件上传(这里使用把选择的文件转成blob演示)
+            file_picker_callback: (callback, value, meta) => {
+                let input = document.createElement('input');
+                input.setAttribute('type', 'file');
+                // 设定文件可选类型
+                if (meta.filetype === 'image') {
+                    input.setAttribute('accept', 'image/*');
+                } else if (meta.filetype === 'media') {
+                    input.setAttribute('accept', 'video/*');
+                }
+                input.onchange = () => {
+                    let file = input.files[0];
+                    let reader = new FileReader();
+                    reader.onload = (e) => {
+                        let blob = new Blob([e.target.result], {type: file.type});
+                        callback(URL.createObjectURL(blob));
+                    };
+                    reader.readAsArrayBuffer(file);
+                }
+                input.click();
+            }
+        }
+    },
+    computed: {
+        ...mapGetters(["permission"]),
+
+        editContent() {
+            return {
+                menubar: false,
+                file_picker_callback: this.file_picker_callback,
+                skin_url: this.$store.state.theme.theme === 'dark' ? '/tinymce/skins/ui/oxide-dark' : '/tinymce/skins/ui/oxide',
+                content_css: this.$store.state.theme.theme === 'dark' ? '/tinymce/skins/content/dark/content.css' : '/tinymce/skins/content/default/content.css'
+            };
+        }
+    },
+    mounted() {
+    },
+    methods: {
+        /* 显示编辑 */
+        edit(row) {
+            this.editForm = Object.assign({}, row);
+            this.showEdit = true;
+        },
+        /* 保存编辑 */
+        save() {
+            this.$refs['editForm'].validate((valid) => {
+                if (valid) {
+                    const loading = this.$loading({lock: true});
+                    this.editForm.attr_type = 2;
+                    this.$http.post('/article/edit', this.editForm).then(res => {
+                        loading.close();
+                        if (res.data.code === 0) {
+                            this.showEdit = false;
+                            this.$message({type: 'success', message: res.data.msg});
+                            this.$refs.table.reload();
+                        } else {
+                            this.$message.error(res.data.msg);
+                        }
+                    }).catch(e => {
+                        loading.close();
+                        this.$message.error(e.message);
+                    });
+                } else {
+                    return false;
+                }
+            });
+        },
+        /* 删除 */
+        remove(row) {
+            if (!row) {  // 批量删除
+                if (this.choose.length === 0) return this.$message.error('请至少选择一条数据');
+                let ids = this.choose.map(d => d.id);
+                this.$confirm('确定要删除选中的文章吗?', '提示', {type: 'warning'}).then(() => {
+                    const loading = this.$loading({lock: true});
+                    this.$http.post('/article/delete', {id: ids}).then(res => {
+                        loading.close();
+                        if (res.data.code === 0) {
+                            this.$message({type: 'success', message: res.data.msg});
+                            this.$refs.table.reload();
+                        } else {
+                            this.$message.error(res.data.msg);
+                        }
+                    }).catch(e => {
+                        loading.close();
+                        this.$message.error(e.message);
+                    });
+                }).catch(() => 0);
+            } else {  // 单个删除
+                const loading = this.$loading({lock: true});
+                this.$http.post('/article/delete', {id: row.id}).then(res => {
+                    loading.close();
+                    if (res.data.code === 0) {
+                        this.$message({type: 'success', message: res.data.msg});
+                        this.$refs.table.reload();
+                    } else {
+                        this.$message.error(res.data.msg);
+                    }
+                }).catch(e => {
+                    loading.close();
+                    this.$message.error(e.message);
+                });
+            }
+        }
+    }
+}
+</script>
+
+<style scoped>
+.ele-block >>> .el-upload, .ele-block >>> .el-upload-dragger {
+    width: 100%;
+}
+</style>

BIN
addons/utcapp/static/icons/alipay_icon.png


+ 14 - 0
vendor/fakerphp/faker/src/Faker/Container/ContainerException.php

@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Faker\Container;
+
+use Psr\Container\ContainerExceptionInterface;
+
+/**
+ * @experimental This class is experimental and does not fall under our BC promise
+ */
+final class ContainerException extends \RuntimeException implements ContainerExceptionInterface
+{
+}

+ 0 - 84
vendor/fakerphp/faker/src/Faker/Extension/ContainerBuilder.php

@@ -1,84 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Faker\Extension;
-
-use Faker\Core;
-use Psr\Container\ContainerInterface;
-
-/**
- * @experimental This class is experimental and does not fall under our BC promise
- */
-final class ContainerBuilder
-{
-    /**
-     * @var array<string, callable|object|string>
-     */
-    private $definitions = [];
-
-    /**
-     * @param callable|object|string $value
-     *
-     * @throws \InvalidArgumentException
-     */
-    public function add($value, string $name = null): self
-    {
-        if (!is_string($value) && !is_callable($value) && !is_object($value)) {
-            throw new \InvalidArgumentException(sprintf(
-                'First argument to "%s::add()" must be a string, callable or object.',
-                self::class
-            ));
-        }
-
-        if ($name === null) {
-            if (is_string($value)) {
-                $name = $value;
-            } elseif (is_object($value)) {
-                $name = get_class($value);
-            } else {
-                throw new \InvalidArgumentException(sprintf(
-                    'Second argument to "%s::add()" is required not passing a string or object as first argument',
-                    self::class
-                ));
-            }
-        }
-
-        $this->definitions[$name] = $value;
-
-        return $this;
-    }
-
-    public function build(): ContainerInterface
-    {
-        return new Container($this->definitions);
-    }
-
-    /**
-     * Get an array with extension that represent the default English
-     * functionality.
-     */
-    public static function defaultExtensions(): array
-    {
-        return [
-            BarcodeExtension::class => Core\Barcode::class,
-            BloodExtension::class => Core\Blood::class,
-            ColorExtension::class => Core\Color::class,
-            FileExtension::class => Core\File::class,
-            NumberExtension::class => Core\Number::class,
-            VersionExtension::class => Core\Version::class,
-            UuidExtension::class => Core\Uuid::class,
-        ];
-    }
-
-    public static function getDefault(): ContainerInterface
-    {
-        $instance = new self();
-
-        foreach (self::defaultExtensions() as $id => $definition) {
-            $instance->add($definition, $id);
-        }
-
-        return $instance->build();
-    }
-}

+ 165 - 0
安装说明.md

@@ -0,0 +1,165 @@
+## 安装说明
+
+### 1、系统运行环境说明
+>1.php版本建议 php7.1+
+> 
+>2.数据库建议 mysql8.0+,mysql5.6也可运行
+> 
+>3.系统要求centos7.5+,4核8G,100G硬盘
+> 
+>4.需要安装nginx,站点服务运行,建议可以[宝塔一键安装](https://www.bt.cn/new/download.html)
+> 
+>5.需要安装composer,用于安装依赖包
+> 
+>6.需要开放的端口:80、443、6520
+>
+>7.需要安装的PHP扩展
+>>+ redis扩展,缓存
+> 
+>>+ gmp扩展,数字钱包加密
+> 
+>>+ swoole扩展,客服聊天
+> 
+>>+ 其他默认必要的扩展
+
+
+### 2、安装步骤
+
+1.解压压缩包代码内容到站点根目录
+
+2.创建数据库,并将压缩包内到数据库文件导入到数据库中
+
+3.配置nginx站点项目伪静态和跨域请求
+```
+location / {
+
+    # 请求地址允许跨域
+    add_header Access-Control-Allow-Origin *;
+    # 请求方法允许跨域
+    add_header Access-Control-Allow-Methods 'GET,POST,OPTIONS,PUT,DELETE,PATCH,Origin';
+    # 设置是否允许 cookie 传输
+    add_header Access-Control-Allow-Credentials 'true';
+    # 设置请求头 这里为什么不设置通配符 * 因为不支持
+    add_header Access-Control-Allow-Headers 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Data-Type,X-Requested-With,X-Data-Type,X-Auth-Token';
+    
+    if ($request_method = 'OPTIONS') {
+	 return 200;
+    }
+
+
+    if (!-e $request_filename){
+	 rewrite  ^(.*)$  /index.php?s=$1  last;   break;
+    }
+}  
+```
+4.配置站点客服请求入口
+```
+# 在站点配置文件中,伪静态配置后添加下面配置
+location /im {
+        proxy_pass http://imCustom;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "upgrade";
+}
+
+# 站点配置文件顶部,server上方添加下面负载均衡节点配置
+upstream imCustom {
+   server 127.0.0.1:6520;
+}
+```
+5. 项目根目录.env配置文件,修改对应配置内容
+```
+#站点配置
+APP_URL=http://test.com   # 站点后端接口地址
+WEB_URL=http://xxx.com/   # 站点前端地址
+
+# 数据库配置
+DB_CONNECTION=mysql
+DB_HOST=localhost
+DB_PORT=3306
+DB_PREFIX=lev_
+DB_DATABASE=nn2022060801  # 修改为新数据库名称
+DB_USERNAME=nn2022060801  # 修改为新数据库账号
+DB_PASSWORD=BAJwCryBsLPFycsF  # 修改为新数据库密码
+
+# 缓存配置
+CACHE_DRIVER=redis  # 当前使用的缓存驱动,file-本地文件缓存,redis-redis缓存
+REDIS_PREFIX=null   # 缓存前缀
+REDIS_HOST=47.112.222.163  # 缓存host
+REDIS_PASSWORD=derkj&6688  # 缓存auth密码
+REDIS_PORT=6379            # 缓存端口
+REDIS_DB=0                  # 缓存数据库ID
+
+#socket客服聊天服务端口配置,默认,修改需要开放端口
+SOCKET_PORT=6520
+
+# 邮件服务默认配置(可后台配置)
+MAIL_DRIVER=smtp  # 默认
+MAIL_HOST=smtp.163.com  # 默认
+MAIL_PORT=25 # 默认
+MAIL_USERNAME=test@163.com  # 发送账号邮箱用户名
+MAIL_FROM_ADDRESS=wesmiler@163.com  # 发送用户邮箱地址
+MAIL_PASSWORD=HGXTVTQDYKWEXEKE   # 发送账号授权密钥
+MAIL_ENCRYPTION=tls     # 加密方式,默认
+```
+
+6.运行客服IM聊天服务
+```
+命令行状态下,进入项目根目录,运行以下命令:
+nohup php artisan swoole:socketIm start >/dev/null 2>&1 & // 后台运行
+nohup php artisan swoole:socketIm stop   // 停止运行
+
+```
+
+### 3、后台程序配置和打包
+1.站点内容目录在addons/admin目录下
+2.进入src/config目录,修改setting.js配置文件
+```
+# 站点接口配置
+baseURL: 'http://test.com/',  // 接口地址
+socketUrl: 'ws://test.com/im',  // Socket聊天客服地址
+```
+3. addons/admin 目录下初始化项目(需先安装nodejs14和vue2.0+运行环境)
+```
+npm install # 初始化项目,安装依赖
+npm run dev  # 本地运行项目
+npm run build # 打包项目
+
+```
+
+4.更新后台打包内容 
+```
+将打包好的内容addons/admin/dist目录下的内容,拷贝到项目根目录下public/admin和public/business内即可
+```
+
+### 4、前端程序配置和打包
+1.站点内容目录在addons/otcapp目录下
+2.使用hbuilder打开该目录项目,修改common/api.js接口配置文件
+```
+# 站点接口配置
+const socketUrl = 'ws://test.com/im'  # 客服聊天地址
+const baseUrl = 'http://test.com' # 站点接口地址
+```
+3. 选中addons/otcapp项目目录,发行为web或h5(需先安装nodejs14和vue2.0+运行环境)
+```
+# 或使用以下命令进行初始化和运行打包
+npm install # 初始化项目,安装依赖
+npm run dev  # 本地运行项目
+npm run build # 打包项目
+
+```
+
+4.更新打包内容
+```
+将打包好的内容addons/otcaapp/unpackage/dist/build/h5目录下的内容,拷贝到项目根目录下public/h5内即可
+```
+5. 访问站点
+```
+总后台地址: http://test.com/admin
+超级管理员账号:otcadm  123456
+
+交易员后台地址: http://test.com/business
+
+H5地址: http://test.com/h5
+可用来打包成APP
+```