浏览代码

✨ 登录接口 投放书籍列表接口

晓晓晓晓丶vv 4 年之前
父节点
当前提交
098e929326

+ 2 - 1
.env.development

@@ -1,3 +1,4 @@
 NODE_ENV = development
 NODE_ENV = development
 VUE_APP_BASE_URL = '/'
 VUE_APP_BASE_URL = '/'
-VUE_APP_PUB_URL = '/'
+VUE_APP_PUB_URL = '/'
+PROXY_API_URL = 'https://t-promoter.58duke.com/'

+ 2 - 1
.env.production

@@ -1,3 +1,4 @@
 NODE_ENV = production
 NODE_ENV = production
 VUE_APP_BASE_URL = '/'
 VUE_APP_BASE_URL = '/'
-VUE_APP_PUB_URL = './'
+VUE_APP_PUB_URL = './'
+PROXY_API_URL = 'https://promoter.58duke.com/'

+ 12 - 1
src/api/config.ts

@@ -1,8 +1,11 @@
 import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
 import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
 import { message } from "ant-design-vue";
 import { message } from "ant-design-vue";
+import store from "@/store";
+import router from "@/router";
 
 
 import { HTTP } from "@/helper/enum";
 import { HTTP } from "@/helper/enum";
 import { IResponse } from "@/types/api";
 import { IResponse } from "@/types/api";
+import { MutationType } from "@/store/modules/app/_type";
 
 
 const instance: AxiosInstance = axios.create({
 const instance: AxiosInstance = axios.create({
   baseURL: "/api",
   baseURL: "/api",
@@ -10,6 +13,8 @@ const instance: AxiosInstance = axios.create({
 
 
 instance.interceptors.request.use(
 instance.interceptors.request.use(
   (config: AxiosRequestConfig): AxiosRequestConfig => {
   (config: AxiosRequestConfig): AxiosRequestConfig => {
+    let user = store.state.app.user;
+    if (user.token) config.headers["x-token"] = user.token;
     return config;
     return config;
   }
   }
 );
 );
@@ -24,8 +29,14 @@ instance.interceptors.response.use(
       case HTTP.success:
       case HTTP.success:
         success = true;
         success = true;
         break;
         break;
+      case HTTP.loginExpire:
+        store.commit(MutationType.setUser, {});
+        router.replace("/login");
+        break;
     }
     }
-    return success ? Promise.resolve(result) : Promise.reject(result);
+    return success
+      ? Promise.resolve(result)
+      : (message.error(result.msg), Promise.reject(result));
   },
   },
   (error) => {
   (error) => {
     if (error?.response?.status === HTTP.serverError)
     if (error?.response?.status === HTTP.serverError)

+ 31 - 0
src/api/index.ts

@@ -0,0 +1,31 @@
+import { AxiosPromise } from "axios";
+import axios from "./config";
+
+// NOTE: 暂时使用ts-ignore忽略module not exported的问题
+// @ts-ignore
+import { IUser, IList, IDeliveryBook } from "@/types/api";
+
+/**
+ * 登录
+ * @param userData 用户信息
+ */
+export const doLogin = <T>(userData: T): AxiosPromise<IUser> => {
+  return axios.post("/login", userData);
+};
+
+/**
+ * 书籍搜素
+ * @param key_word
+ * @param page
+ */
+export const getDeliveryBookList = (
+  query: Partial<{
+    official_name: string;
+    book_name: string;
+    page: 1;
+  }>
+): AxiosPromise<IList<IDeliveryBook>> => {
+  return axios("/getUserDeliveryBooks", {
+    params: query,
+  });
+};

+ 18 - 3
src/components/tool-bar/index.vue

@@ -15,7 +15,9 @@
     <slot />
     <slot />
     <div class="tool-bar-item">
     <div class="tool-bar-item">
       <a-button type="primary"
       <a-button type="primary"
-                size="large">
+                size="large"
+                :loading="btn_loading"
+                @click="onConfirm">
         <template #icon>
         <template #icon>
           <search-outlined />
           <search-outlined />
         </template>
         </template>
@@ -26,7 +28,7 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
-import { defineComponent, PropType, ref } from "vue";
+import { defineComponent, PropType, ref, watchEffect } from "vue";
 import { SearchOutlined } from "@ant-design/icons-vue";
 import { SearchOutlined } from "@ant-design/icons-vue";
 
 
 const ToolBar = defineComponent({
 const ToolBar = defineComponent({
@@ -42,9 +44,14 @@ const ToolBar = defineComponent({
       type: Object as PropType<string[]>,
       type: Object as PropType<string[]>,
       default: () => ref<string[]>([]),
       default: () => ref<string[]>([]),
     },
     },
+    loading: {
+      type: Boolean,
+      default: false,
+    },
   },
   },
   setup(props, { emit, slots }) {
   setup(props, { emit, slots }) {
     const fields = ref<{ [key: string]: string }>({});
     const fields = ref<{ [key: string]: string }>({});
+    let btn_loading = ref(props.loading);
 
 
     let showPickerSlots = ref(!!slots.picker);
     let showPickerSlots = ref(!!slots.picker);
 
 
@@ -52,7 +59,15 @@ const ToolBar = defineComponent({
       fields.value[field] = "";
       fields.value[field] = "";
     });
     });
 
 
-    return { fields, showPickerSlots };
+    watchEffect(() => (btn_loading.value = props.loading));
+
+    const onConfirm = () => {
+      btn_loading.value = true;
+      emit("update:loading", btn_loading.value);
+      emit("confirm", fields.value);
+    };
+
+    return { fields, showPickerSlots, btn_loading, onConfirm };
   },
   },
 });
 });
 
 

+ 1 - 0
src/helper/enum.ts

@@ -1,4 +1,5 @@
 export enum HTTP {
 export enum HTTP {
   success = 0,
   success = 0,
+  loginExpire = 1006,
   serverError = 500,
   serverError = 500,
 }
 }

+ 9 - 0
src/helper/index.ts

@@ -1,6 +1,15 @@
 const path = require("path");
 const path = require("path");
 
 
 /**
 /**
+ * json字符串格式化
+ */
+String.prototype.parse = function<T>(this: string): T | null {
+  let result: T | null = null;
+  if (this) result = JSON.parse(this);
+  return result;
+};
+
+/**
  * 事件监听
  * 事件监听
  * @param el 监听dom
  * @param el 监听dom
  * @param cb 回调
  * @param cb 回调

+ 30 - 26
src/router/permission.ts

@@ -5,11 +5,11 @@ import store from "@/store";
 
 
 import Layout from "@/layout/index.vue";
 import Layout from "@/layout/index.vue";
 
 
-import { RouteConfig } from "@/types/route";
 import { ActionType } from "@/store/modules/app/_type";
 import { ActionType } from "@/store/modules/app/_type";
 import { NotFound } from "./modules/constant";
 import { NotFound } from "./modules/constant";
+import { RouteConfig } from "@/types/route";
 
 
-const WHITE_URL_LIST: string[] = ["/login", "/forget"];
+const WHITE_URL_LIST: string[] = ["/login"];
 
 
 /**
 /**
  * 未登录的情况下 路由的处理
  * 未登录的情况下 路由的处理
@@ -36,30 +36,34 @@ router.beforeEach(
     _: RouteLocationNormalized,
     _: RouteLocationNormalized,
     next: NavigationGuardNext
     next: NavigationGuardNext
   ) => {
   ) => {
-    // if (to.path === "/login") next({ path: "/", replace: true });
-    // else {
-    // }
-    const roles = store.state.app.roles;
-    if (roles.length) next();
-    else {
-      const userRoles: string[] = await store.dispatch(ActionType.getUserRoles);
-      const webRoutes: RouteConfig[] = await store.dispatch(
-        ActionType.generatorRoutes,
-        userRoles
-      );
-      const AppRoute: RouteConfig = {
-        name: "home",
-        path: "",
-        redirect: webRoutes[0].path,
-        component: Layout,
-        children: webRoutes,
-      };
-      router.addRoute(AppRoute);
-      // 添加全局Not Found Page
-      router.addRoute(NotFound);
-      next({ ...to, replace: true, name: to.name! });
-    }
-    //  redirectRoutes(to, next);
+    const user = store.state.app.user;
+    if (user.token) {
+      if (to.path === "/login") next({ path: "/", replace: true });
+      else {
+        const roles = store.state.app.roles;
+        if (roles.length) next();
+        else {
+          const userRoles: string[] = await store.dispatch(
+            ActionType.getUserRoles
+          );
+          const webRoutes: RouteConfig[] = await store.dispatch(
+            ActionType.generatorRoutes,
+            userRoles
+          );
+          const AppRoute: RouteConfig = {
+            name: "home",
+            path: "",
+            redirect: webRoutes[0].path,
+            component: Layout,
+            children: webRoutes,
+          };
+          router.addRoute(AppRoute);
+          // 添加全局Not Found Page
+          router.addRoute(NotFound);
+          next({ ...to, replace: true, name: to.name! });
+        }
+      }
+    } else redirectRoutes(to, next);
   }
   }
 );
 );
 
 

+ 9 - 0
src/store/modules/app/_type.ts

@@ -2,28 +2,35 @@ import { AugmentedActionContext } from "./_module";
 import { RouteConfig } from "@/types/route";
 import { RouteConfig } from "@/types/route";
 import { RouteRecordRaw } from "vue-router";
 import { RouteRecordRaw } from "vue-router";
 
 
+import { IUser } from "@/types/api";
+
 // state 定义
 // state 定义
 type State = {
 type State = {
+  store: Storage;
   roles: string[];
   roles: string[];
   routes: RouteConfig[];
   routes: RouteConfig[];
   addRoutes: RouteConfig[];
   addRoutes: RouteConfig[];
+  user: Partial<IUser>;
 };
 };
 
 
 // mutation 定义
 // mutation 定义
 enum MutationType {
 enum MutationType {
   setRoles = "setRoles",
   setRoles = "setRoles",
   setRoutes = "setRoutes",
   setRoutes = "setRoutes",
+  setUser = "setUser",
 }
 }
 
 
 type Mutations<S = State> = {
 type Mutations<S = State> = {
   [MutationType.setRoles](state: S, roles: string[]): void;
   [MutationType.setRoles](state: S, roles: string[]): void;
   [MutationType.setRoutes](state: S, routes: RouteConfig[]): void;
   [MutationType.setRoutes](state: S, routes: RouteConfig[]): void;
+  [MutationType.setUser](state: S, user: Partial<IUser>): void;
 };
 };
 
 
 // action 定义
 // action 定义
 enum ActionType {
 enum ActionType {
   generatorRoutes = "generatorRoutes",
   generatorRoutes = "generatorRoutes",
   getUserRoles = "getUserRoles",
   getUserRoles = "getUserRoles",
+  doLogin = "doLogin",
 }
 }
 
 
 type Actions = {
 type Actions = {
@@ -34,12 +41,14 @@ type Actions = {
   [ActionType.getUserRoles]({
   [ActionType.getUserRoles]({
     commit,
     commit,
   }: AugmentedActionContext): Promise<string[]>;
   }: AugmentedActionContext): Promise<string[]>;
+  [ActionType.doLogin]({ commit }: AugmentedActionContext, user: unknown): void;
 };
 };
 
 
 // getter 定义
 // getter 定义
 type Getters = {
 type Getters = {
   permissionRoutes(state: State): RouteRecordRaw[];
   permissionRoutes(state: State): RouteRecordRaw[];
   userRoles(state: State): string[];
   userRoles(state: State): string[];
+  user(state: State): Partial<IUser>;
 };
 };
 
 
 export { State, Mutations, MutationType, Actions, ActionType, Getters };
 export { State, Mutations, MutationType, Actions, ActionType, Getters };

+ 15 - 3
src/store/modules/app/actions.ts

@@ -4,13 +4,17 @@ import { RootState } from "@/store";
 import asyncRoutes from "@/router/async";
 import asyncRoutes from "@/router/async";
 import { filterRoutesByRoles } from "@/helper/permission";
 import { filterRoutesByRoles } from "@/helper/permission";
 
 
+import { doLogin } from "@/api";
+
+type userData = {
+  account: string;
+  passwd: string;
+};
+
 const actions: ActionTree<State, RootState> & Actions = {
 const actions: ActionTree<State, RootState> & Actions = {
   [ActionType.generatorRoutes]({ commit }, roles) {
   [ActionType.generatorRoutes]({ commit }, roles) {
     return new Promise(async (resolve) => {
     return new Promise(async (resolve) => {
       let resultRoutes = filterRoutesByRoles(asyncRoutes, roles) || [];
       let resultRoutes = filterRoutesByRoles(asyncRoutes, roles) || [];
-      // if (roles.includes("admin"))
-      //   resultRoutes = filterRoutesByRoles(asyncRoutes, roles) || [];
-      // else resultRoutes = filterRoutesByRoles(asyncRoutes, roles) || [];
       await commit(MutationType.setRoutes, resultRoutes);
       await commit(MutationType.setRoutes, resultRoutes);
       resolve(resultRoutes);
       resolve(resultRoutes);
     });
     });
@@ -22,6 +26,14 @@ const actions: ActionTree<State, RootState> & Actions = {
       resolve(roles);
       resolve(roles);
     });
     });
   },
   },
+  async [ActionType.doLogin]({ commit }, user: userData) {
+    try {
+      const { data } = await doLogin<userData>(user);
+      commit(MutationType.setUser, data);
+    } catch (e) {
+      console.log("error", e);
+    }
+  },
 };
 };
 
 
 export default actions;
 export default actions;

+ 1 - 0
src/store/modules/app/getters.ts

@@ -5,6 +5,7 @@ import { Getters, State } from "./_type";
 const getters: GetterTree<State, RootState> & Getters = {
 const getters: GetterTree<State, RootState> & Getters = {
   permissionRoutes: (state) => state.routes,
   permissionRoutes: (state) => state.routes,
   userRoles: (state) => state.roles,
   userRoles: (state) => state.roles,
+  user: (state) => state.user,
 };
 };
 
 
 export default getters;
 export default getters;

+ 4 - 0
src/store/modules/app/mutations.ts

@@ -8,6 +8,10 @@ const mutations: MutationTree<State> & Mutations = {
   [MutationType.setRoutes](state, routes) {
   [MutationType.setRoutes](state, routes) {
     state.routes = routes;
     state.routes = routes;
   },
   },
+  [MutationType.setUser](state, user) {
+    state.user = user;
+    state.store.setItem("user", JSON.stringify(user));
+  },
 };
 };
 
 
 export default mutations;
 export default mutations;

+ 15 - 0
src/store/modules/app/state.ts

@@ -1,9 +1,24 @@
 import { State } from "./_type";
 import { State } from "./_type";
+import { IUser } from "@/types/api";
+
+/**
+ * TODO: 待优化
+ * json字符串格式化
+ */
+String.prototype.parse = function<T>(this: string): T | null {
+  let result: T | null = null;
+  if (this) result = JSON.parse(this);
+  return result;
+};
+
+const store = localStorage;
 
 
 const state: State = {
 const state: State = {
+  store: store,
   roles: [],
   roles: [],
   routes: [],
   routes: [],
   addRoutes: [],
   addRoutes: [],
+  user: store.getItem("user")?.parse<IUser>() ?? {},
 };
 };
 
 
 export default state;
 export default state;

+ 41 - 0
src/types/api.d.ts

@@ -3,3 +3,44 @@ export interface IResponse<T> extends Promise<T> {
   data?: T;
   data?: T;
   msg: string;
   msg: string;
 }
 }
+
+export interface IUser {
+  uid: number | string;
+  nickname: string;
+  token: string;
+}
+
+export interface IMeta {
+  current_page: number;
+  next_page: number;
+  last_page: number;
+  per_page: number;
+  total: number;
+  is_end: boolean;
+  next_page_url: string;
+  prev_page_url: string;
+}
+
+export interface IList<T> {
+  meta: IMeta;
+  list: T[];
+}
+
+export interface IBookSearchResult {
+  id: number;
+  bid: string;
+  name: string;
+  platform: string;
+}
+
+export interface IDeliveryBook {
+  id: number;
+  bid: number | string;
+  book_name: string;
+  delivery_bid: number;
+  delivery_platform: string;
+  status: number;
+  start_time: string;
+  end_time: string;
+  official_name: string;
+}

+ 3 - 0
src/types/window.d.ts

@@ -4,4 +4,7 @@ declare global {
   declare interface Window {
   declare interface Window {
     __APP__: App<Element>;
     __APP__: App<Element>;
   }
   }
+  declare interface String {
+    parse<T>(): T | null | undefined;
+  }
 }
 }

+ 14 - 7
src/views/Login.vue

@@ -5,7 +5,7 @@
       <a-form :model="forms"
       <a-form :model="forms"
               v-bind="layout">
               v-bind="layout">
         <a-form-item>
         <a-form-item>
-          <a-input v-model:value="forms.username"
+          <a-input v-model:value="forms.account"
                    size="large"
                    size="large"
                    placeholder="请输入用户名">
                    placeholder="请输入用户名">
             <template #prefix>
             <template #prefix>
@@ -14,7 +14,7 @@
           </a-input>
           </a-input>
         </a-form-item>
         </a-form-item>
         <a-form-item>
         <a-form-item>
-          <a-input-password v-model:value="forms.password"
+          <a-input-password v-model:value="forms.passwd"
                             size="large"
                             size="large"
                             type="password"
                             type="password"
                             placeholder="请输入密码">
                             placeholder="请输入密码">
@@ -28,6 +28,7 @@
                     block
                     block
                     size="large"
                     size="large"
                     shape="round"
                     shape="round"
+                    :loading="loading"
                     @click="onLogin">登录</a-button>
                     @click="onLogin">登录</a-button>
         </a-form-item>
         </a-form-item>
       </a-form>
       </a-form>
@@ -41,6 +42,7 @@ import { UserOutlined, LockOutlined } from "@ant-design/icons-vue";
 
 
 import useFormLayout from "@/hooks/useFormLayout";
 import useFormLayout from "@/hooks/useFormLayout";
 import useApp from "@/hooks/useApp";
 import useApp from "@/hooks/useApp";
+import { ActionType } from "@/store/modules/app/_type";
 
 
 const Login = defineComponent({
 const Login = defineComponent({
   name: "Login",
   name: "Login",
@@ -49,18 +51,22 @@ const Login = defineComponent({
     LockOutlined,
     LockOutlined,
   },
   },
   setup() {
   setup() {
-    const { router } = useApp();
+    const { router, store } = useApp();
     const formLayout = useFormLayout(6);
     const formLayout = useFormLayout(6);
 
 
     const data = reactive({
     const data = reactive({
       forms: {
       forms: {
-        username: "",
-        password: "",
+        account: "xiaowei",
+        passwd: "655c98362b4bd6fa",
       },
       },
+      loading: false,
       layout: formLayout,
       layout: formLayout,
     });
     });
 
 
-    const onLogin = () => {
+    const onLogin = async () => {
+      data.loading = true;
+      await store.dispatch(ActionType.doLogin, data.forms);
+      data.loading = false;
       router.replace("/");
       router.replace("/");
     };
     };
 
 
@@ -74,7 +80,8 @@ export default Login;
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .login-wrap {
 .login-wrap {
   height: 100vh;
   height: 100vh;
-  background: url(http://normal-image.xiaovv-web.com/normal/2020-11-13-background.svg) #f0f2f5 no-repeat top left / contain;
+  background: url(http://normal-image.xiaovv-web.com/normal/2020-11-13-background.svg)
+    #f0f2f5 no-repeat top left / contain;
 
 
   .title {
   .title {
     font-size: 40px;
     font-size: 40px;

+ 2 - 6
src/views/_pageOptions/table-put.ts

@@ -1,19 +1,15 @@
 export const TableColumnOfPutBooks = [
 export const TableColumnOfPutBooks = [
   {
   {
     title: "公众号名称",
     title: "公众号名称",
-    dataIndex: "name",
+    dataIndex: "official_name",
   },
   },
   {
   {
     title: "书籍",
     title: "书籍",
     dataIndex: "book_name",
     dataIndex: "book_name",
   },
   },
   {
   {
-    title: "推广员",
-    dataIndex: "promoter",
-  },
-  {
     title: "流量平台",
     title: "流量平台",
-    dataIndex: "platform",
+    dataIndex: "delivery_platform",
   },
   },
 
 
   {
   {

+ 29 - 7
src/views/put/put-book.vue

@@ -1,30 +1,52 @@
 <template>
 <template>
   <div class="page-wrap page-wrap-put-books">
   <div class="page-wrap page-wrap-put-books">
-    <tool-bar :text="['name', 'book_name']"
-              :label="['公众号名称', '书名']" />
-    <a-table :columns="columns"
+    <tool-bar :text="['official_name', 'book_name']"
+              :label="['公众号名称', '书名']"
+              v-model:loading="inSearching"
+              @confirm="onSearch" />
+    <a-table row-key="id"
+             :columns="columns"
              :data-source="list"></a-table>
              :data-source="list"></a-table>
   </div>
   </div>
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
-import { defineComponent, reactive, toRefs } from "vue";
+import { defineComponent, reactive, ref, toRefs } from "vue";
 
 
 import ToolBar from "@/components/tool-bar/index.vue";
 import ToolBar from "@/components/tool-bar/index.vue";
 
 
+import { getDeliveryBookList } from "@/api";
 import { TableColumnOfPutBooks } from "../_pageOptions/table-put";
 import { TableColumnOfPutBooks } from "../_pageOptions/table-put";
+import { IDeliveryBook } from "@/types/api";
 
 
 const PutBooks = defineComponent({
 const PutBooks = defineComponent({
   components: {
   components: {
     ToolBar,
     ToolBar,
   },
   },
   setup() {
   setup() {
-    const data = reactive({
-      list: [],
+    const bookData = reactive({
+      inSearching: false,
+      list: ref<IDeliveryBook[]>([]),
       columns: TableColumnOfPutBooks,
       columns: TableColumnOfPutBooks,
     });
     });
 
 
-    return { ...toRefs(data) };
+    const onSearch = async (fields: Record<string, string>) => {
+      try {
+        const { official_name, book_name } = fields;
+        const { data } = await getDeliveryBookList({
+          official_name,
+          book_name,
+          page: 1,
+        });
+        bookData.list = data.list;
+      } catch (e) {
+        console.log(e);
+      } finally {
+        bookData.inSearching = false;
+      }
+    };
+
+    return { ...toRefs(bookData), onSearch };
   },
   },
 });
 });
 
 

+ 1 - 5
vue.config.js

@@ -9,11 +9,7 @@ module.exports = {
     // * 接口跨域处理
     // * 接口跨域处理
     proxy: {
     proxy: {
       "/api": {
       "/api": {
-        target: "https://channelpre2.aizhuishu.com",
-        changeOrigin: true,
-      },
-      "/channel/img": {
-        target: "https://channelpre2.aizhuishu.com",
+        target: process.env.PROXY_API_URL,
         changeOrigin: true,
         changeOrigin: true,
       },
       },
     },
     },