pansl 2 سال پیش
والد
کامیت
742e0942db

+ 1 - 1
src/i18n/languages/en.ts

@@ -1,6 +1,6 @@
 const en = {
   system: {
-    name: '书城新平台管理系统',
+    name: '追书云短剧平台',
     chinese: 'Chinese',
     english: 'English',
     confirm: 'Confirm',

+ 1 - 1
src/i18n/languages/zh.ts

@@ -1,6 +1,6 @@
 const zh = {
   system: {
-    name: '书城新平台管理系统',
+    name: '追书云短剧平台',
     chinese: '中文',
     english: '英文',
     confirm: '确定',

+ 20 - 0
src/router/modules/applet.ts

@@ -0,0 +1,20 @@
+import { RouteRecordRaw } from 'vue-router';
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+const router: RouteRecordRaw[] = [
+  {
+    path: '/users',
+    component: () => import('@/layout/index.vue'),
+    meta: { title: '小程序管理', icon: 'user' },
+    children: [
+      {
+        path: 'applet',
+        name: 'applet-account',
+        meta: { title: '小程序管理', icon: 'home' },
+        component: () => import('@/views/appletManage/applet/index.vue')
+      }
+    ]
+  }
+];
+
+export default router;

+ 20 - 0
src/router/modules/caster.ts

@@ -0,0 +1,20 @@
+import { RouteRecordRaw } from 'vue-router';
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+const router: RouteRecordRaw[] = [
+  {
+    path: '/users',
+    component: () => import('@/layout/index.vue'),
+    meta: { title: '用户管理', icon: 'user' },
+    children: [
+      {
+        path: 'index',
+        name: 'user-account',
+        meta: { title: '添加投手', icon: 'home' },
+        component: () => import('@/views/user/caster/index.vue')
+      }
+    ]
+  }
+];
+
+export default router;

+ 1 - 1
src/stores/modules/user/permissions.ts

@@ -145,7 +145,7 @@ export const usePermissionsStore = defineStore('PermissionsStore', {
           }
           const menu: Menu = Object.assign({
             path: this.resolveRoutePathRoutePath(permission.route, path),
-            name: permission.module + '_' + permission.permission_mark + "-" + permission.id,
+            name: permission.module + '_' + permission.permission_mark + "_" + permission.id,
             component: importComponent,
             redirect: permission.redirect,
             meta: Object.assign({

+ 158 - 0
src/views/appletManage/applet/form/create.vue

@@ -0,0 +1,158 @@
+<template>
+  <el-form :model="formData" label-width="100px" ref="ruleForm" :rules="rules" v-loading="loading" class="pr-4">
+    <div class="flex flex-row justify-between">
+      <div class="w-full">
+        <el-form-item label="登录账号" prop="email">
+          <el-input v-model="formData.email" placeholder="请填写登录账号" />
+        </el-form-item>
+        <el-form-item label="用户名" prop="username">
+          <el-input v-model="formData.username" placeholder="请填写用户名" />
+        </el-form-item>
+        <el-form-item label="密码" prop="password">
+          <el-input v-model="formData.password" clearable type="password" autocomplete="new-password" size="large"
+            placeholder="密码" show-password :prefix-icon="Lock" class="h-12 text-base" />
+        </el-form-item>
+        <el-form-item label="确认密码" prop="password_confirmation">
+          <el-input v-model="formData.password_confirmation" clearable type="password" autocomplete="new-password"
+            size="large" placeholder="确认密码" show-password :prefix-icon="Lock" class="h-12 text-base" />
+        </el-form-item>
+        <el-form-item label="绑定小程序" prop="applet">
+          <el-select v-model="formData.applet" remote filterable :remote-method="remoteMethod" clearable
+            placeholder="选择CP方">
+            <el-option v-for="item in cpList" :key="item.cp_id" :label="item.cp_name" :value="item.cp_name" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态" prop="username">
+          <el-switch v-model="formData.delivery" />
+        </el-form-item>
+        <el-form-item label="备注" prop="username">
+          <el-input v-model="formData.username" placeholder="请填写备注" type="textarea" />
+        </el-form-item>
+      </div>
+    </div>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{ $t('system.confirm') }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { useCreate } from '@/hook/curd/useCreate';
+import { useShow } from '@/hook/curd/useShow';
+import { cpManageCpList, cpOptions } from '@/api/cp/index'
+import type { FormInstance, FormRules } from 'element-plus'
+const ruleForm = ref<FormInstance>()
+
+import { onMounted, ref } from 'vue';
+import http from '@/support/http';
+const props = defineProps({
+  primary: String | Number,
+  api: String,
+});
+
+//自定义校验规则
+const validatePasswordConfirmation = (
+  rule: any,
+  value: any,
+  callback: any
+) => {
+  if (value === '') {
+    callback(new Error('请再次输入密码'));
+  } else if (value !== ruleForm.password) {
+    callback(new Error('两次密码不匹配'));
+  } else {
+    callback();
+  }
+};
+const rules = reactive({
+  applet: [{ required: true, message: '请选择小程序' }],
+  email: [
+    {
+      required: true,
+      message: '请输入邮箱',
+      trigger: 'blur'
+    },
+    {
+      type: 'email',
+      message: '邮箱格式不正确',
+      trigger: 'blur'
+    }
+  ],
+  code: [
+    {
+      required: true,
+      message: '请输入验证码',
+      trigger: 'blur'
+    }
+  ],
+  password: [
+    {
+      required: true,
+      message: '请输入密码',
+      trigger: 'blur'
+    },
+    {
+      pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/,
+      message:
+        '必须包含大小写字母和数字的组合,可以使用特殊字符,长度在6-20之间'
+    }
+  ],
+  password_confirmation: [
+    {
+      required: true,
+      message: '请再次输入密码',
+      trigger: 'blur'
+    },
+    { validator: validatePasswordConfirmation, trigger: 'blur' }
+  ]
+});
+
+
+
+
+const remoteMethod = (query: string) => {
+  if (query) {
+    initCpOtion({ cp_name: query })
+  } else {
+    initCpOtion({})
+  }
+}
+
+const initCpOtion = (params: object) => {
+  cpOptions(params).then(res => {
+    cpList.value = res.data;
+  })
+}
+
+const passwordRules = [
+  {
+    required: true,
+    message: '密码必须填写'
+  },
+  {
+    pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/,
+    message: '必须包含大小写字母和数字的组合,可以使用特殊字符,长度在6-20之间'
+  }
+];
+
+if (props.primary) {
+  passwordRules.shift();
+}
+
+const { formData, form, loading, submitForm, close } = useCreate(
+  props.api,
+  props.primary
+);
+
+if (props.primary) {
+  useShow(props.api, props.primary, formData);
+}
+
+const emit = defineEmits(['close']);
+close(() => emit('close'));
+const cpList = ref();
+
+onMounted(() => {
+  initCpOtion({})
+});
+</script>

+ 86 - 0
src/views/appletManage/applet/form/depotsTransfer.vue

@@ -0,0 +1,86 @@
+<template>
+  <el-form :model="form" label-width="80px" ref="ruleForm" :rules="rules" v-loading="loading" class="pr-4">
+    <div class="flex flex-row justify-between">
+      <div class="w-full">
+        <el-form-item label="登录账号" prop="email">
+          <el-input v-model="form.email" placeholder="请填写登录账号" />
+        </el-form-item>
+        <el-form-item label="状态" prop="username">
+          <el-switch v-model="form.delivery" />
+        </el-form-item>
+        <el-form-item label="备注" prop="username">
+          <el-transfer v-model="form.depotsValue" :props="{
+            key: 'channel_id',
+            label: 'remark',
+          }" :titles="['未分配', '已分配']" filterable @right-check-change="transferChange" :filter-method="filterMethod"
+            filter-placeholder="搜索" :data="dataObj" />
+        </el-form-item>
+      </div>
+    </div>
+    <div class="flex justify-end">
+      <el-button type="primary" size="default" @click="doSave">确定</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { bookDistribute, bookDistributeSave } from '@/api/bookManage/index';
+
+const props = defineProps({
+  primary: Object,
+});
+const form = ref({})
+const dataObj = ref([])
+const channels = ref([])
+const emit = defineEmits(['close']);
+const transferChange = (e) => {
+  let arr = JSON.parse(JSON.stringify(dataObj.value))
+  const checkArr = arr.filter(el => e.includes(el.channel_id)).map(el => {
+    return {
+      channel_id: el.channel_id,
+      is_enabled: el.is_enabled
+    }
+  })
+  console.log(channels);
+  channels.value = checkArr
+
+}
+const filterMethod = (query, item) => {
+  return item.remark.includes(query)
+}
+const doSave = () => {
+  console.log(form.value.depotsValue, 'channels.value', toRaw(channels.value));
+  if (toRaw(channels.value).length <= 0) {
+    ElMessage.warning('请选择商户')
+  } else {
+    bookDistributeSave(props.primary.id, { channels: channels.value }).then(res => {
+      console.log(res);
+      ElMessage.success(res.message)
+      emit('close', false)
+    })
+  }
+}
+
+
+onMounted(async () => {
+  let { data } = await bookDistribute(props.primary.id, {});
+  console.log(data);
+  dataObj.value = data
+})
+
+</script>
+
+<style lang="scss" scoped>
+::v-deep(.el-input__wrapper) {
+  display: flex;
+  width: 180px;
+  flex: none;
+}
+
+.wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+}
+</style>

+ 95 - 0
src/views/appletManage/applet/index.vue

@@ -0,0 +1,95 @@
+<template>
+  <div class="flex flex-col justify-between w-full sm:flex-row">
+    <!-- <Department v-model="query.department_id" @searchDepartmentUsers="search" v-if="hasRoles" class="dark:bg-regal-dark" /> -->
+    <div :class="hasRoles ? 'w-full ml-0 sm:ml-2 mt-2 sm:mt-0' : 'w-full'">
+      <Search :search="search" :reset="reset">
+        <template v-slot:body>
+          <el-form-item label="小程序名称">
+            <el-input v-model="query.email" clearable />
+          </el-form-item>
+          <el-form-item label="所属公司">
+            <el-input v-model="query.username" clearable />
+          </el-form-item>
+          <el-form-item label="对应剧场名称">
+            <el-input v-model="query.username" clearable />
+          </el-form-item>
+        </template>
+      </Search>
+      <div class="table-default">
+        <Operate :show="open" />
+        <el-table :data="tableData" class="mt-3" v-loading="loading">
+          <el-table-column prop="username" label="ID" />
+          <el-table-column prop="username" label="小程序名称" />
+          <el-table-column prop="username" label="所属公司" />
+          <el-table-column prop="username" label="对应剧场名称" />
+          <el-table-column prop="status" label="状态">
+            <template #default="scope">
+              <Status v-model="scope.row.status" :id="scope.row.id" :api="api" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="created_at" label="类型" />
+          <el-table-column label="操作" width="200">
+            <template #default="scope">
+              <el-button link type="primary" size="small" @click="opendepots(scope.row)">分配</el-button>
+              <el-button link type="primary" size="small" @click="open(scope.row.id)">更新</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <Paginate />
+      </div>
+      <Dialog width="800px" v-model="depotsVisible" title="分配" destroy-on-close>
+        <depotsTransfer @close="closeDeptos()" :primary="depotsData"></depotsTransfer>
+      </Dialog>
+
+      <Dialog v-model="visible" :title="title" destroy-on-close>
+        <Create @close="close(reset)" :primary="id" :api="api" :has-roles="hasRoles" />
+      </Dialog>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue';
+import Create from './form/create.vue';
+import depotsTransfer from './form/depotsTransfer.vue';
+import { useGetList } from '@/hook/curd/useGetList';
+import { useDestroy } from '@/hook/curd/useDestroy';
+import { useOpen } from '@/hook/curd/useOpen';
+import Department from './components/department.vue';
+import { useUserStore } from '@/stores/modules/user';
+import { isUndefined } from '@/support/helper';
+
+const userStore = useUserStore();
+const applet = ref([{ id: 1, name: '微信', value: 'wx' }, { id: 2, name: '抖音', value: 'dy' }])
+
+const api = 'users';
+const depotsVisible = ref(false)
+const depotsData = ref({})
+
+const { data, query, search, reset, loading } = useGetList(api);
+const { destroy, deleted } = useDestroy();
+const { open, close, title, visible, id } = useOpen();
+
+const tableData = computed(() => data.value?.data);
+
+const roles = ref<Array<Object>>();
+const hasRoles = ref<boolean>(false);
+
+const opendepots = (data) => {
+  depotsVisible.value = true
+  depotsData.value = data
+}
+const closeDeptos = () => {
+  depotsVisible.value = false
+  search()
+}
+
+onMounted(() => {
+  search();
+
+  deleted(reset);
+
+  hasRoles.value = !isUndefined(userStore.getRoles);
+});
+</script>

+ 1 - 1
src/views/dashboard/index.vue

@@ -3,7 +3,7 @@
     <div class="welcome">
       <div class="header">
         <!-- <img src="@/assets/logo.png" alt="logo" class="logo"> -->
-        <h1>书城新平台管理系统</h1>
+        <h1>追书云短剧平台</h1>
       </div>
       <div class="content">
         <h2>欢迎使用</h2>

+ 158 - 0
src/views/user/caster/create.vue

@@ -0,0 +1,158 @@
+<template>
+  <el-form :model="formData" label-width="100px" ref="ruleForm" :rules="rules" v-loading="loading" class="pr-4">
+    <div class="flex flex-row justify-between">
+      <div class="w-full">
+        <el-form-item label="登录账号" prop="email">
+          <el-input v-model="formData.email" placeholder="请填写登录账号" />
+        </el-form-item>
+        <el-form-item label="用户名" prop="username">
+          <el-input v-model="formData.username" placeholder="请填写用户名" />
+        </el-form-item>
+        <el-form-item label="密码" prop="password">
+          <el-input v-model="formData.password" clearable type="password" autocomplete="new-password" size="large"
+            placeholder="密码" show-password :prefix-icon="Lock" class="h-12 text-base" />
+        </el-form-item>
+        <el-form-item label="确认密码" prop="password_confirmation">
+          <el-input v-model="formData.password_confirmation" clearable type="password" autocomplete="new-password"
+            size="large" placeholder="确认密码" show-password :prefix-icon="Lock" class="h-12 text-base" />
+        </el-form-item>
+        <el-form-item label="绑定小程序" prop="applet">
+          <el-select v-model="formData.applet" remote filterable :remote-method="remoteMethod" clearable
+            placeholder="选择CP方">
+            <el-option v-for="item in cpList" :key="item.cp_id" :label="item.cp_name" :value="item.cp_name" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态" prop="username">
+          <el-switch v-model="formData.delivery" />
+        </el-form-item>
+        <el-form-item label="备注" prop="username">
+          <el-input v-model="formData.username" placeholder="请填写备注" type="textarea" />
+        </el-form-item>
+      </div>
+    </div>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{ $t('system.confirm') }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { useCreate } from '@/hook/curd/useCreate';
+import { useShow } from '@/hook/curd/useShow';
+import { cpManageCpList, cpOptions } from '@/api/cp/index'
+import type { FormInstance, FormRules } from 'element-plus'
+const ruleForm = ref<FormInstance>()
+
+import { onMounted, ref } from 'vue';
+import http from '@/support/http';
+const props = defineProps({
+  primary: String | Number,
+  api: String,
+});
+
+//自定义校验规则
+const validatePasswordConfirmation = (
+  rule: any,
+  value: any,
+  callback: any
+) => {
+  if (value === '') {
+    callback(new Error('请再次输入密码'));
+  } else if (value !== ruleForm.password) {
+    callback(new Error('两次密码不匹配'));
+  } else {
+    callback();
+  }
+};
+const rules = reactive({
+  applet: [{ required: true, message: '请选择小程序' }],
+  email: [
+    {
+      required: true,
+      message: '请输入邮箱',
+      trigger: 'blur'
+    },
+    {
+      type: 'email',
+      message: '邮箱格式不正确',
+      trigger: 'blur'
+    }
+  ],
+  code: [
+    {
+      required: true,
+      message: '请输入验证码',
+      trigger: 'blur'
+    }
+  ],
+  password: [
+    {
+      required: true,
+      message: '请输入密码',
+      trigger: 'blur'
+    },
+    {
+      pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/,
+      message:
+        '必须包含大小写字母和数字的组合,可以使用特殊字符,长度在6-20之间'
+    }
+  ],
+  password_confirmation: [
+    {
+      required: true,
+      message: '请再次输入密码',
+      trigger: 'blur'
+    },
+    { validator: validatePasswordConfirmation, trigger: 'blur' }
+  ]
+});
+
+
+
+
+const remoteMethod = (query: string) => {
+  if (query) {
+    initCpOtion({ cp_name: query })
+  } else {
+    initCpOtion({})
+  }
+}
+
+const initCpOtion = (params: object) => {
+  cpOptions(params).then(res => {
+    cpList.value = res.data;
+  })
+}
+
+const passwordRules = [
+  {
+    required: true,
+    message: '密码必须填写'
+  },
+  {
+    pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/,
+    message: '必须包含大小写字母和数字的组合,可以使用特殊字符,长度在6-20之间'
+  }
+];
+
+if (props.primary) {
+  passwordRules.shift();
+}
+
+const { formData, form, loading, submitForm, close } = useCreate(
+  props.api,
+  props.primary
+);
+
+if (props.primary) {
+  useShow(props.api, props.primary, formData);
+}
+
+const emit = defineEmits(['close']);
+close(() => emit('close'));
+const cpList = ref();
+
+onMounted(() => {
+  initCpOtion({})
+});
+</script>

+ 81 - 0
src/views/user/caster/index.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="flex flex-col justify-between w-full sm:flex-row">
+    <!-- <Department v-model="query.department_id" @searchDepartmentUsers="search" v-if="hasRoles" class="dark:bg-regal-dark" /> -->
+    <div :class="hasRoles ? 'w-full ml-0 sm:ml-2 mt-2 sm:mt-0' : 'w-full'">
+      <Search :search="search" :reset="reset">
+        <template v-slot:body>
+          <el-form-item label="账号">
+            <el-input v-model="query.email" clearable />
+          </el-form-item>
+          <el-form-item label="用户名">
+            <el-input v-model="query.username" clearable />
+          </el-form-item>
+          <el-form-item label="小程序">
+            <el-select v-model="query.applet" filterable clearable placeholder="选择小程序">
+              <el-option v-for="item in applet" :key="item.id" :label="item.name" :value="item.value" />
+            </el-select>
+          </el-form-item>
+        </template>
+      </Search>
+      <div class="table-default">
+        <Operate :show="open" />
+        <el-table :data="tableData" class="mt-3" v-loading="loading">
+          <el-table-column prop="username" label="ID" />
+          <el-table-column prop="username" label="账号" />
+          <el-table-column prop="username" label="用户名" />
+          <el-table-column prop="username" label="已绑定小程序" />
+          <el-table-column prop="status" label="状态">
+            <template #default="scope">
+              <Status v-model="scope.row.status" :id="scope.row.id" :api="api" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="created_at" label="创建时间" />
+          <el-table-column label="操作" width="200">
+            <template #default="scope">
+              <Update @click="open(scope.row.id)" />
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <Paginate />
+      </div>
+
+      <Dialog v-model="visible" :title="title" destroy-on-close>
+        <Create @close="close(reset)" :primary="id" :api="api" :has-roles="hasRoles" />
+      </Dialog>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue';
+import Create from './create.vue';
+import { useGetList } from '@/hook/curd/useGetList';
+import { useDestroy } from '@/hook/curd/useDestroy';
+import { useOpen } from '@/hook/curd/useOpen';
+import Department from './components/department.vue';
+import { useUserStore } from '@/stores/modules/user';
+import { isUndefined } from '@/support/helper';
+
+const userStore = useUserStore();
+const applet = ref([{ id: 1, name: '微信', value: 'wx' }, { id: 2, name: '抖音', value: 'dy' }])
+
+const api = 'users';
+
+const { data, query, search, reset, loading } = useGetList(api);
+const { destroy, deleted } = useDestroy();
+const { open, close, title, visible, id } = useOpen();
+
+const tableData = computed(() => data.value?.data);
+
+const roles = ref<Array<Object>>();
+const hasRoles = ref<boolean>(false);
+
+onMounted(() => {
+  search();
+
+  deleted(reset);
+
+  hasRoles.value = !isUndefined(userStore.getRoles);
+});
+</script>