feat: 新增新手任务以及新手引导功能
|
@ -0,0 +1,23 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class NoviceStatistics implements Serializable {
|
||||
private String id;
|
||||
|
||||
private String userId;
|
||||
|
||||
private Integer guideStep;
|
||||
|
||||
private Integer guideNum;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
private Long updateTime;
|
||||
|
||||
private String dataOption;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -0,0 +1,580 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NoviceStatisticsExample {
|
||||
protected String orderByClause;
|
||||
|
||||
protected boolean distinct;
|
||||
|
||||
protected List<Criteria> oredCriteria;
|
||||
|
||||
public NoviceStatisticsExample() {
|
||||
oredCriteria = new ArrayList<Criteria>();
|
||||
}
|
||||
|
||||
public void setOrderByClause(String orderByClause) {
|
||||
this.orderByClause = orderByClause;
|
||||
}
|
||||
|
||||
public String getOrderByClause() {
|
||||
return orderByClause;
|
||||
}
|
||||
|
||||
public void setDistinct(boolean distinct) {
|
||||
this.distinct = distinct;
|
||||
}
|
||||
|
||||
public boolean isDistinct() {
|
||||
return distinct;
|
||||
}
|
||||
|
||||
public List<Criteria> getOredCriteria() {
|
||||
return oredCriteria;
|
||||
}
|
||||
|
||||
public void or(Criteria criteria) {
|
||||
oredCriteria.add(criteria);
|
||||
}
|
||||
|
||||
public Criteria or() {
|
||||
Criteria criteria = createCriteriaInternal();
|
||||
oredCriteria.add(criteria);
|
||||
return criteria;
|
||||
}
|
||||
|
||||
public Criteria createCriteria() {
|
||||
Criteria criteria = createCriteriaInternal();
|
||||
if (oredCriteria.size() == 0) {
|
||||
oredCriteria.add(criteria);
|
||||
}
|
||||
return criteria;
|
||||
}
|
||||
|
||||
protected Criteria createCriteriaInternal() {
|
||||
Criteria criteria = new Criteria();
|
||||
return criteria;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
oredCriteria.clear();
|
||||
orderByClause = null;
|
||||
distinct = false;
|
||||
}
|
||||
|
||||
protected abstract static class GeneratedCriteria {
|
||||
protected List<Criterion> criteria;
|
||||
|
||||
protected GeneratedCriteria() {
|
||||
super();
|
||||
criteria = new ArrayList<Criterion>();
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return criteria.size() > 0;
|
||||
}
|
||||
|
||||
public List<Criterion> getAllCriteria() {
|
||||
return criteria;
|
||||
}
|
||||
|
||||
public List<Criterion> getCriteria() {
|
||||
return criteria;
|
||||
}
|
||||
|
||||
protected void addCriterion(String condition) {
|
||||
if (condition == null) {
|
||||
throw new RuntimeException("Value for condition cannot be null");
|
||||
}
|
||||
criteria.add(new Criterion(condition));
|
||||
}
|
||||
|
||||
protected void addCriterion(String condition, Object value, String property) {
|
||||
if (value == null) {
|
||||
throw new RuntimeException("Value for " + property + " cannot be null");
|
||||
}
|
||||
criteria.add(new Criterion(condition, value));
|
||||
}
|
||||
|
||||
protected void addCriterion(String condition, Object value1, Object value2, String property) {
|
||||
if (value1 == null || value2 == null) {
|
||||
throw new RuntimeException("Between values for " + property + " cannot be null");
|
||||
}
|
||||
criteria.add(new Criterion(condition, value1, value2));
|
||||
}
|
||||
|
||||
public Criteria andIdIsNull() {
|
||||
addCriterion("id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdIsNotNull() {
|
||||
addCriterion("id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdEqualTo(String value) {
|
||||
addCriterion("id =", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotEqualTo(String value) {
|
||||
addCriterion("id <>", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdGreaterThan(String value) {
|
||||
addCriterion("id >", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("id >=", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLessThan(String value) {
|
||||
addCriterion("id <", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("id <=", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLike(String value) {
|
||||
addCriterion("id like", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotLike(String value) {
|
||||
addCriterion("id not like", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdIn(List<String> values) {
|
||||
addCriterion("id in", values, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotIn(List<String> values) {
|
||||
addCriterion("id not in", values, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdBetween(String value1, String value2) {
|
||||
addCriterion("id between", value1, value2, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotBetween(String value1, String value2) {
|
||||
addCriterion("id not between", value1, value2, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdIsNull() {
|
||||
addCriterion("user_id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdIsNotNull() {
|
||||
addCriterion("user_id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdEqualTo(String value) {
|
||||
addCriterion("user_id =", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdNotEqualTo(String value) {
|
||||
addCriterion("user_id <>", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdGreaterThan(String value) {
|
||||
addCriterion("user_id >", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("user_id >=", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdLessThan(String value) {
|
||||
addCriterion("user_id <", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("user_id <=", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdLike(String value) {
|
||||
addCriterion("user_id like", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdNotLike(String value) {
|
||||
addCriterion("user_id not like", value, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdIn(List<String> values) {
|
||||
addCriterion("user_id in", values, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdNotIn(List<String> values) {
|
||||
addCriterion("user_id not in", values, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdBetween(String value1, String value2) {
|
||||
addCriterion("user_id between", value1, value2, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUserIdNotBetween(String value1, String value2) {
|
||||
addCriterion("user_id not between", value1, value2, "userId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepIsNull() {
|
||||
addCriterion("guide_step is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepIsNotNull() {
|
||||
addCriterion("guide_step is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepEqualTo(Integer value) {
|
||||
addCriterion("guide_step =", value, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepNotEqualTo(Integer value) {
|
||||
addCriterion("guide_step <>", value, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepGreaterThan(Integer value) {
|
||||
addCriterion("guide_step >", value, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepGreaterThanOrEqualTo(Integer value) {
|
||||
addCriterion("guide_step >=", value, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepLessThan(Integer value) {
|
||||
addCriterion("guide_step <", value, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepLessThanOrEqualTo(Integer value) {
|
||||
addCriterion("guide_step <=", value, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepIn(List<Integer> values) {
|
||||
addCriterion("guide_step in", values, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepNotIn(List<Integer> values) {
|
||||
addCriterion("guide_step not in", values, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepBetween(Integer value1, Integer value2) {
|
||||
addCriterion("guide_step between", value1, value2, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideStepNotBetween(Integer value1, Integer value2) {
|
||||
addCriterion("guide_step not between", value1, value2, "guideStep");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumIsNull() {
|
||||
addCriterion("guide_num is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumIsNotNull() {
|
||||
addCriterion("guide_num is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumEqualTo(Integer value) {
|
||||
addCriterion("guide_num =", value, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumNotEqualTo(Integer value) {
|
||||
addCriterion("guide_num <>", value, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumGreaterThan(Integer value) {
|
||||
addCriterion("guide_num >", value, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumGreaterThanOrEqualTo(Integer value) {
|
||||
addCriterion("guide_num >=", value, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumLessThan(Integer value) {
|
||||
addCriterion("guide_num <", value, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumLessThanOrEqualTo(Integer value) {
|
||||
addCriterion("guide_num <=", value, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumIn(List<Integer> values) {
|
||||
addCriterion("guide_num in", values, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumNotIn(List<Integer> values) {
|
||||
addCriterion("guide_num not in", values, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumBetween(Integer value1, Integer value2) {
|
||||
addCriterion("guide_num between", value1, value2, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andGuideNumNotBetween(Integer value1, Integer value2) {
|
||||
addCriterion("guide_num not between", value1, value2, "guideNum");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeIsNull() {
|
||||
addCriterion("create_time is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeIsNotNull() {
|
||||
addCriterion("create_time is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeEqualTo(Long value) {
|
||||
addCriterion("create_time =", value, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeNotEqualTo(Long value) {
|
||||
addCriterion("create_time <>", value, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeGreaterThan(Long value) {
|
||||
addCriterion("create_time >", value, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) {
|
||||
addCriterion("create_time >=", value, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeLessThan(Long value) {
|
||||
addCriterion("create_time <", value, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeLessThanOrEqualTo(Long value) {
|
||||
addCriterion("create_time <=", value, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeIn(List<Long> values) {
|
||||
addCriterion("create_time in", values, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeNotIn(List<Long> values) {
|
||||
addCriterion("create_time not in", values, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeBetween(Long value1, Long value2) {
|
||||
addCriterion("create_time between", value1, value2, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateTimeNotBetween(Long value1, Long value2) {
|
||||
addCriterion("create_time not between", value1, value2, "createTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeIsNull() {
|
||||
addCriterion("update_time is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeIsNotNull() {
|
||||
addCriterion("update_time is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeEqualTo(Long value) {
|
||||
addCriterion("update_time =", value, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeNotEqualTo(Long value) {
|
||||
addCriterion("update_time <>", value, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeGreaterThan(Long value) {
|
||||
addCriterion("update_time >", value, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) {
|
||||
addCriterion("update_time >=", value, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeLessThan(Long value) {
|
||||
addCriterion("update_time <", value, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeLessThanOrEqualTo(Long value) {
|
||||
addCriterion("update_time <=", value, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeIn(List<Long> values) {
|
||||
addCriterion("update_time in", values, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeNotIn(List<Long> values) {
|
||||
addCriterion("update_time not in", values, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeBetween(Long value1, Long value2) {
|
||||
addCriterion("update_time between", value1, value2, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andUpdateTimeNotBetween(Long value1, Long value2) {
|
||||
addCriterion("update_time not between", value1, value2, "updateTime");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
||||
protected Criteria() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criterion {
|
||||
private String condition;
|
||||
|
||||
private Object value;
|
||||
|
||||
private Object secondValue;
|
||||
|
||||
private boolean noValue;
|
||||
|
||||
private boolean singleValue;
|
||||
|
||||
private boolean betweenValue;
|
||||
|
||||
private boolean listValue;
|
||||
|
||||
private String typeHandler;
|
||||
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object getSecondValue() {
|
||||
return secondValue;
|
||||
}
|
||||
|
||||
public boolean isNoValue() {
|
||||
return noValue;
|
||||
}
|
||||
|
||||
public boolean isSingleValue() {
|
||||
return singleValue;
|
||||
}
|
||||
|
||||
public boolean isBetweenValue() {
|
||||
return betweenValue;
|
||||
}
|
||||
|
||||
public boolean isListValue() {
|
||||
return listValue;
|
||||
}
|
||||
|
||||
public String getTypeHandler() {
|
||||
return typeHandler;
|
||||
}
|
||||
|
||||
protected Criterion(String condition) {
|
||||
super();
|
||||
this.condition = condition;
|
||||
this.typeHandler = null;
|
||||
this.noValue = true;
|
||||
}
|
||||
|
||||
protected Criterion(String condition, Object value, String typeHandler) {
|
||||
super();
|
||||
this.condition = condition;
|
||||
this.value = value;
|
||||
this.typeHandler = typeHandler;
|
||||
if (value instanceof List<?>) {
|
||||
this.listValue = true;
|
||||
} else {
|
||||
this.singleValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected Criterion(String condition, Object value) {
|
||||
this(condition, value, null);
|
||||
}
|
||||
|
||||
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
|
||||
super();
|
||||
this.condition = condition;
|
||||
this.value = value;
|
||||
this.secondValue = secondValue;
|
||||
this.typeHandler = typeHandler;
|
||||
this.betweenValue = true;
|
||||
}
|
||||
|
||||
protected Criterion(String condition, Object value, Object secondValue) {
|
||||
this(condition, value, secondValue, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,8 +31,10 @@
|
|||
"pinia": "^2.0.14",
|
||||
"pinia-plugin-persistedstate": "^1.6.3",
|
||||
"qiankun": ">2.10.1 || 2.9.3",
|
||||
"shepherd.js": "^10.0.0",
|
||||
"vue": "^2.7.3",
|
||||
"vue-i18n": "^8.22.4",
|
||||
"vue-shepherd": "^0.3.0",
|
||||
"vuedraggable": "^2.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import {get, post} from "../plugins/request"
|
||||
import {getCurrentUserId} from "../utils/token";
|
||||
import {TASK_DATA} from "../utils/constants";
|
||||
|
||||
|
||||
export function getSideTask() {
|
||||
return post(`/novice/info`);
|
||||
}
|
||||
|
||||
export function saveStep() {
|
||||
return post(`/novice/save/step`,{'guideStep': localStorage.getItem('step')});
|
||||
}
|
||||
|
||||
export function saveTask(data) {
|
||||
return post(`/novice/save/task`,{'dataOption': JSON.stringify(data)});
|
||||
}
|
||||
|
||||
export function updateUserByResourceId(resourceId) {
|
||||
let userId = getCurrentUserId();
|
||||
return get(`/user/update/current-by-resource/${resourceId}`);
|
||||
}
|
||||
|
||||
export function initTaskData(url){
|
||||
getSideTask().then(res=>{
|
||||
let taskData = TASK_DATA
|
||||
if(res.data.length > 0 && res.data[0].dataOption){
|
||||
taskData = JSON.parse(res.data[0].dataOption)
|
||||
}
|
||||
if(taskData.length > 0){
|
||||
taskData.forEach(item=>{
|
||||
let index = item.taskData.findIndex(function(res) {
|
||||
return res.status === 0 && res.api.includes(url);
|
||||
});
|
||||
if(index > -1){
|
||||
item.taskData[index].status = 1
|
||||
item.rate += 1
|
||||
item.percentage = Math.floor(item.rate / item.taskData.length * 100)
|
||||
if(item.percentage === 100){
|
||||
item.status = 1
|
||||
}else if(100 > item.percentage && item.percentage > 0){
|
||||
item.status = 2
|
||||
}
|
||||
}
|
||||
})
|
||||
// 入库
|
||||
saveTask(taskData).then(res => {
|
||||
}).catch(error => {
|
||||
// 错误的信息
|
||||
this.$error({
|
||||
message: error.response.data.message
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
After Width: | Height: | Size: 434 KiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 4.7 MiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 4.6 MiB |
After Width: | Height: | Size: 2.9 MiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 380 KiB |
After Width: | Height: | Size: 3.3 MiB |
After Width: | Height: | Size: 1024 KiB |
After Width: | Height: | Size: 2.6 MiB |
After Width: | Height: | Size: 3.5 MiB |
After Width: | Height: | Size: 2.5 MiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 361 KiB |
After Width: | Height: | Size: 4.1 MiB |
After Width: | Height: | Size: 3.8 MiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 3.1 MiB |
After Width: | Height: | Size: 274 KiB |
After Width: | Height: | Size: 4.6 MiB |
After Width: | Height: | Size: 6.3 MiB |
After Width: | Height: | Size: 3.6 MiB |
After Width: | Height: | Size: 155 KiB |
|
@ -0,0 +1,152 @@
|
|||
@import '~shepherd.js/dist/css/shepherd.css';
|
||||
.shepherd-has-title.shepherd-element {
|
||||
width: 220px !important;
|
||||
}
|
||||
.custom-width {
|
||||
width: 320px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom"] {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom-start"] {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom-end"] {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="left"] {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="left-start"] {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="left-end"] {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="right"] {
|
||||
margin-left: 20px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="right-start"] {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="right-end"] {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="top"] {
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="top-start"] {
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="top-end"] {
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom"]>.shepherd-arrow {
|
||||
left: 10px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom-end"]>.shepherd-arrow {
|
||||
left: -50px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom-start"]>.shepherd-arrow {
|
||||
left: -20px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="top-end"]>.shepherd-arrow {
|
||||
left: 150px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="top-start"]>.shepherd-arrow {
|
||||
left: -150px !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="right"]>.shepherd-arrow {
|
||||
left: 0 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="right-start"]>.shepherd-arrow {
|
||||
left: 0 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="right-end"]>.shepherd-arrow {
|
||||
left: 0 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="left"]>.shepherd-arrow {
|
||||
right: 0 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="left-start"]>.shepherd-arrow {
|
||||
right: 0 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="left-end"]>.shepherd-arrow {
|
||||
right: 0 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shepherd-arrow:before {
|
||||
top: 4px;
|
||||
left: -5px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom"]>.shepherd-arrow:before {
|
||||
background-color: #783787 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom-end"]>.shepherd-arrow:before {
|
||||
background-color: #783787 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element[data-popper-placement="bottom-start"]>.shepherd-arrow:before {
|
||||
background-color: #783787 !important;
|
||||
}
|
||||
|
||||
.shepherd-has-title.shepherd-element .shepherd-header {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 1em;
|
||||
background-color: #fff;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shepherd-header .shepherd-title {
|
||||
/*margin-top: 1.2em;*/
|
||||
font-size: 16px !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shepherd-text {
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
padding: 10px 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shepherd-footer {
|
||||
padding: 0 1em 1em;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shepherd-footer .shep-btn {
|
||||
height: 32px;
|
||||
padding: 0 1px;
|
||||
line-height: 32px;
|
||||
background-color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shepherd-footer .close-btn {
|
||||
height: 32px;
|
||||
padding: 0 1px;
|
||||
line-height: 32px;
|
||||
color: #868bac !important;
|
||||
background-color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .close-btn:hover {
|
||||
color: #505266 !important;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shep-btn {
|
||||
border: none;
|
||||
color: #783787;
|
||||
}
|
||||
.shepherd-has-title.shepherd-element .shep-btn:hover {
|
||||
transform: scale(1.1) !important;
|
||||
}
|
||||
|
||||
.shepherd-title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.shepherd-has-title.shepherd-element .shepherd-header {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 1em;
|
||||
background-color: #783787;
|
||||
}
|
||||
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
<el-container>
|
||||
<el-aside
|
||||
:class="[isCollapse ? 'ms-aside': 'ms-aside-collapse-open', isFullScreen ? 'is-fullscreen' : '']"
|
||||
class="ms-left-aside"
|
||||
class="ms-left-aside shepherd-menu"
|
||||
:style="isFixed ? 'opacity:100%; position: relative;z-index: 666;': 'opacity: 95%;position: fixed'"
|
||||
@mouseenter.native="collapseOpen"
|
||||
@mouseleave.native="collapseClose">
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
<template>
|
||||
<el-dropdown size="medium" @command="handleCommand" class="ms-header-menu align-right">
|
||||
<span class="dropdown-link">
|
||||
<font-awesome-icon class="icon global focusing" :icon="['fas', 'question-circle']" />
|
||||
</span>
|
||||
<template v-slot:dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="guidance">{{ $t('commons.page_guidance') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="introduction">{{ $t('commons.function_introduction') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="novice">{{ $t('commons.novice_journey') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
<ms-introduction ref="introduction" @skipOpen="skipOpen"/>
|
||||
<ms-side-menus ref="sideMenu"/>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsIntroduction from "../../components/guide/components/Introduction";
|
||||
import MsSideMenus from "../../components/sidemenu/SideMenus";
|
||||
import {getSideTask} from "../../api/novice";
|
||||
|
||||
|
||||
export default {
|
||||
name: "MsGuidance",
|
||||
components: {MsIntroduction,MsSideMenus},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.introduction.resVisible = localStorage.getItem("introduction") !== 'false' &&
|
||||
(localStorage.getItem("guide") === '1' || localStorage.getItem("step") > 1);
|
||||
this.checkStep()
|
||||
},
|
||||
methods: {
|
||||
handleCommand(command) {
|
||||
switch (command) {
|
||||
case "introduction":
|
||||
this.$refs.introduction.openNext();
|
||||
break;
|
||||
case "guidance":
|
||||
localStorage.setItem("guide", 0)
|
||||
localStorage.removeItem('step')
|
||||
if(this.$route.path.includes('project')){
|
||||
this.$router.push('/project/home')
|
||||
this.$router.go(0);
|
||||
}else{
|
||||
this.$router.push('/project/home')
|
||||
}
|
||||
break;
|
||||
case "novice":
|
||||
this.$refs.sideMenu.toggle();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
checkStep(){
|
||||
getSideTask().then(res=> {
|
||||
if (res.data.length > 0 && res.data[0].guideStep) {
|
||||
localStorage.setItem('step', res.data[0].guideStep)
|
||||
} else {
|
||||
localStorage.setItem('guide','0')
|
||||
localStorage.removeItem('step')
|
||||
}
|
||||
let microApps = JSON.parse(sessionStorage.getItem("micro_apps"));
|
||||
if(localStorage.getItem("guide") === '0' && microApps && microApps['project']) {
|
||||
localStorage.setItem("step", '1')
|
||||
this.initStep()
|
||||
}
|
||||
})
|
||||
},
|
||||
initStep() {
|
||||
const _this = this
|
||||
_this.$nextTick(() => {
|
||||
const tour = _this.$shepherd({
|
||||
useModalOverlay: true,
|
||||
exitOnEsc: false,
|
||||
keyboardNavigation: false,
|
||||
defaultStepOptions: {
|
||||
scrollTo: {
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
},
|
||||
canClickTarget: false,
|
||||
// 高亮元素四周要填充的空白像素
|
||||
modalOverlayOpeningPadding: 0,
|
||||
// 空白像素的圆角
|
||||
modalOverlayOpeningRadius: 4
|
||||
}
|
||||
})
|
||||
tour.addSteps([
|
||||
{
|
||||
attachTo: {
|
||||
element: document.querySelector('.shepherd-workspace'),
|
||||
on: 'bottom-start'
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
action: function() {
|
||||
_this.$refs.introduction.resVisible = localStorage.getItem("step") > 1
|
||||
return _this.gotoCancel(this, true)
|
||||
},
|
||||
classes: 'close-btn',
|
||||
text: _this.$t("shepherd.exit")
|
||||
},
|
||||
{
|
||||
action: function() {
|
||||
return _this.gotoNext(this, null, 2)
|
||||
},
|
||||
classes: 'shep-btn',
|
||||
text: _this.$t("shepherd.next")
|
||||
}
|
||||
],
|
||||
title: _this.$t("shepherd.step1.title"),
|
||||
text: _this.$t("shepherd.step1.text")
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: document.querySelector('.shepherd-menu'),
|
||||
on: 'right'
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
action: function() {
|
||||
_this.$refs.introduction.resVisible = localStorage.getItem("step") > 1
|
||||
return _this.gotoCancel(this, true)
|
||||
},
|
||||
classes: 'close-btn',
|
||||
text: _this.$t("shepherd.exit")
|
||||
},
|
||||
{
|
||||
action: function() {
|
||||
return _this.gotoNext(this, '/project/home', 3)
|
||||
},
|
||||
classes: 'shep-btn',
|
||||
text: _this.$t("shepherd.next")
|
||||
}
|
||||
],
|
||||
title: _this.$t("shepherd.step2.title"),
|
||||
text: _this.$t("shepherd.step2.text")
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: document.querySelector('.shepherd-project'),
|
||||
on: 'bottom-end'
|
||||
},
|
||||
classes: "custom-width",
|
||||
buttons: [
|
||||
{
|
||||
action: function() {
|
||||
_this.$refs.introduction.resVisible = localStorage.getItem("step") > 1
|
||||
return _this.gotoCancel(this, true)
|
||||
},
|
||||
classes: 'close-btn',
|
||||
text: _this.$t("shepherd.exit")
|
||||
},
|
||||
{
|
||||
action: function() {
|
||||
return _this.gotoNext(this, null, 4)
|
||||
},
|
||||
classes: 'shep-btn',
|
||||
text: _this.$t("shepherd.next")
|
||||
}
|
||||
],
|
||||
title: _this.$t("shepherd.step3.title"),
|
||||
text: _this.$t("shepherd.step3.text")
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: document.querySelector('.shepherd-project-menu'),
|
||||
on: 'bottom-start'
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
action: function() {
|
||||
_this.$refs.introduction.resVisible = localStorage.getItem("step") > 1
|
||||
return _this.gotoCancel(this, true)
|
||||
},
|
||||
classes: 'close-btn',
|
||||
text: _this.$t("shepherd.exit")
|
||||
},
|
||||
{
|
||||
action: function() {
|
||||
return _this.gotoNext(this, '/project/home', 5)
|
||||
},
|
||||
classes: 'shep-btn',
|
||||
text: _this.$t("shepherd.next")
|
||||
}
|
||||
],
|
||||
title: _this.$t("shepherd.step4.title"),
|
||||
text: _this.$t("shepherd.step4.text")
|
||||
},
|
||||
{
|
||||
arrow:true,
|
||||
modalOverlayOpeningPadding: 8,
|
||||
attachTo: {
|
||||
element: document.querySelector('.shepherd-project-name'),
|
||||
on: 'right'
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
action: function() {
|
||||
_this.$refs.introduction.resVisible = localStorage.getItem("step") > 1
|
||||
return _this.gotoCancel(this, false)
|
||||
},
|
||||
classes: 'close-btn',
|
||||
text: _this.$t("shepherd.know")
|
||||
}
|
||||
],
|
||||
title: _this.$t("shepherd.step5.title"),
|
||||
text: _this.$t("shepherd.step5.text")
|
||||
}
|
||||
])
|
||||
tour.start()
|
||||
})
|
||||
},
|
||||
skipOpen(path){
|
||||
if(path){
|
||||
this.$refs.sideMenu.skipOpen(path);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dropdown-link {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: rgb(146, 147, 150);
|
||||
}
|
||||
|
||||
.align-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.ms-header-menu {
|
||||
padding-top: 12px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.ms-header-menu:hover {
|
||||
cursor: pointer;
|
||||
border-color: var(--color);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog :close-on-click-modal="false" z-index="1000" :width="nextVisible ? '50%' : '40%'"
|
||||
:visible.sync="resVisible" :class="nextVisible ? 'api-import-next' : 'api-import'" destroy-on-close @close="closeDialog">
|
||||
<div class="card" v-if="!nextVisible">
|
||||
<img src="/assets/guide/visual-collaboration.png" class="image" alt="MS">
|
||||
<div class="content">
|
||||
<p class="title" ><img src="../../../assets/guide/hard.png" alt="MS">{{ $t("guide.home.title") }}</p>
|
||||
<div class="bottom clearfix">
|
||||
<p class="desc">{{ $t("guide.home.desc") }}</p>
|
||||
<el-button type="primary" round size="small" class="button" @click="openNext">
|
||||
{{ $t("guide.home.button") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-next" v-else>
|
||||
<el-carousel trigger="click" :autoplay="false" :loop="false" indicator-position="outside" arrow="never"
|
||||
height="350px" ref="carousel" @change="toggleCarousel">
|
||||
<el-carousel-item v-for="item in carouseData" :key="item.id" >
|
||||
<img :src="'/assets/guide/' + item.url" class="image-next" alt="MS">
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
<template v-for="item in carouseData">
|
||||
<div class="bottom clearfix" :key="item.id" v-if="item.id === carouselId">
|
||||
<p class="title-next">{{ $t(item.title) }}</p>
|
||||
<p class="desc-next" v-html="$t(item.desc)" />
|
||||
<el-row>
|
||||
<el-col v-if="carouselId === 3" :span="24">
|
||||
<el-button type="primary" round size="small" class="button" @click="gotoTurn()">
|
||||
{{ $t(item.button) }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<template v-else>
|
||||
<el-col :span="8">
|
||||
<br v-if="carouselId === 0">
|
||||
<el-button type="primary" v-else round size="small" class="is-plain"
|
||||
@click="toggleCarousel(carouselId - 1)">
|
||||
{{ $t("guide.go_prev") }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<br>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" round size="small" class="button" @click="toggleCarousel(carouselId + 1)">
|
||||
{{ $t(item.button) }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsIntroduction",
|
||||
data() {
|
||||
return {
|
||||
carouselId: 0,
|
||||
resVisible: false,
|
||||
nextVisible: false,
|
||||
carouseData: [
|
||||
{
|
||||
url: 'track.png',
|
||||
title: 'guide.test.title',
|
||||
desc: 'guide.test.desc',
|
||||
button: 'guide.test.button',
|
||||
id: 0
|
||||
},
|
||||
{
|
||||
url: 'api.png',
|
||||
title: 'guide.api.title',
|
||||
desc: 'guide.api.desc',
|
||||
button: 'guide.api.button',
|
||||
id: 1
|
||||
},
|
||||
{
|
||||
url: 'ui.png',
|
||||
title: 'guide.ui.title',
|
||||
desc: 'guide.ui.desc',
|
||||
button: 'guide.ui.button',
|
||||
id: 2
|
||||
},
|
||||
{
|
||||
url: 'performance.png',
|
||||
title: 'guide.performance.title',
|
||||
desc: 'guide.performance.desc',
|
||||
button: 'guide.performance.button',
|
||||
id: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
openNext() {
|
||||
this.carouselId = 0
|
||||
this.resVisible = true;
|
||||
this.nextVisible = true;
|
||||
},
|
||||
closeDialog() {
|
||||
localStorage.setItem("introduction", 'false');
|
||||
this.resVisible = false;
|
||||
this.nextVisible = false;
|
||||
},
|
||||
toggleCarousel(index){
|
||||
this.carouselId = index
|
||||
this.$refs.carousel.setActiveItem(index);
|
||||
},
|
||||
gotoTurn(){
|
||||
this.closeDialog()
|
||||
let redirectUrl = sessionStorage.getItem("redirectUrl")
|
||||
if(redirectUrl.includes("track")){
|
||||
this.$emit("skipOpen", "/track/case/all")
|
||||
this.$router.push("/track/case/all")
|
||||
// this.$router.go(0);
|
||||
}else{
|
||||
this.$router.push({
|
||||
path: '/track/case/all',
|
||||
query: { status: true },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
::v-deep .api-import .el-dialog {
|
||||
border-radius: 8px;
|
||||
background-image: linear-gradient(to bottom, #f4f4f4 45%, #FFF 0);
|
||||
}
|
||||
|
||||
::v-deep .api-import .el-dialog__body {
|
||||
padding: 1px 5px 0 5px;
|
||||
}
|
||||
::v-deep .api-import-next .el-dialog__body {
|
||||
padding: 12px 5px 0 5px;
|
||||
}
|
||||
|
||||
::v-deep .api-import-next .el-dialog {
|
||||
margin-top: 10vh !important;
|
||||
border-radius: 8px;
|
||||
background-image: linear-gradient(to bottom, #783887 65%, #FFF 0);
|
||||
}
|
||||
|
||||
::v-deep .api-import-next .el-dialog__headerbtn:focus .el-dialog__close {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep .api-import-next .el-dialog__headerbtn:hover .el-dialog__close {
|
||||
color: #fff !important;
|
||||
}
|
||||
.card {
|
||||
height: 350px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 217px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 220%;
|
||||
font-weight: 700;
|
||||
}
|
||||
.title img {
|
||||
width: 53px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.card-next {
|
||||
height: 560px;
|
||||
text-align: center;
|
||||
}
|
||||
.image-next {
|
||||
width: 600px;
|
||||
height: 300px;
|
||||
padding-top: 5px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #fff;
|
||||
box-shadow: 0px 0px 10px hsl(0deg 0% 100%);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.title-next {
|
||||
margin: 0;
|
||||
font-size: x-large;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.desc-next {
|
||||
line-height: initial;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
padding: 0 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
<style scoped>
|
||||
::v-deep .el-carousel__indicator--horizontal .el-carousel__button {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #8c8c8c;
|
||||
border: 1px solid #ffffff;
|
||||
border-radius: 50%;
|
||||
opacity: 0.5;
|
||||
}
|
||||
::v-deep .el-carousel__indicator--horizontal.is-active .el-carousel__button {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #783887;
|
||||
border-radius: 50%;
|
||||
opacity: 1;
|
||||
|
||||
}
|
||||
|
||||
::v-deep .el-dialog__headerbtn {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-dropdown size="medium" @command="changeWs" placement="bottom" class="align-right"
|
||||
<el-dropdown size="medium" @command="changeWs" placement="bottom" class="align-right shepherd-workspace"
|
||||
v-permission="['PROJECT_TRACK_CASE:READ','PROJECT_TRACK_PLAN:READ','PROJECT_TRACK_REVIEW:READ',
|
||||
'PROJECT_API_DEFINITION:READ','PROJECT_API_SCENARIO:READ','PROJECT_API_REPORT:READ',
|
||||
'PROJECT_USER:READ', 'PROJECT_ENVIRONMENT:READ', 'PROJECT_FILE:READ+JAR', 'PROJECT_FILE:READ+FILE', 'PROJECT_OPERATING_LOG:READ', 'PROJECT_CUSTOM_CODE:READ',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" default-active="1" router>
|
||||
<el-menu class="header-menu shepherd-project" :unique-opened="true" mode="horizontal" default-active="1" router>
|
||||
<!-- 不激活项目路由-->
|
||||
<el-menu-item index="1" v-show="false">Placeholder</el-menu-item>
|
||||
<el-submenu index="2" popper-class="submenu">
|
||||
|
@ -24,6 +24,7 @@
|
|||
<span style="padding-left: 7px;">{{ $t('commons.show_all') }}</span>
|
||||
</el-menu-item>
|
||||
</el-submenu>
|
||||
<ms-introduction ref="introduction"/>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
|
@ -31,13 +32,15 @@
|
|||
import ProjectSearchList from "./ProjectSearchList";
|
||||
import {PROJECT_NAME} from "../../utils/constants";
|
||||
import {getCurrentProjectID} from "../../utils/token";
|
||||
import MsIntroduction from "../../components/guide/components/Introduction";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ProjectSwitch",
|
||||
props: {
|
||||
projectName: String
|
||||
},
|
||||
components: {ProjectSearchList},
|
||||
components: {ProjectSearchList,MsIntroduction},
|
||||
watch: {
|
||||
currentProject() {
|
||||
sessionStorage.setItem(PROJECT_NAME, this.currentProject);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<ms-header-ws/>
|
||||
<ms-task-center/>
|
||||
<ms-notification/>
|
||||
<ms-guidance/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -14,6 +15,7 @@ import MsHeaderWs from "../../components/head/HeaderWs";
|
|||
import MsLanguageSwitch from "../../components/head/LanguageSwitch";
|
||||
import MsTaskCenter from "../../components/task/TaskCenter";
|
||||
import MsNotification from "../../components/notice/Notification";
|
||||
import MsGuidance from "../../components/guide/Guidance";
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -24,7 +26,8 @@ export default {
|
|||
MsTaskCenter,
|
||||
MsLanguageSwitch,
|
||||
MsUser,
|
||||
MsHeaderWs
|
||||
MsHeaderWs,
|
||||
MsGuidance
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<div v-if="taskStatus">
|
||||
<!-- 侧边任务按钮-->
|
||||
<div class="parentBox" @click="toggle()">
|
||||
<div class="contentsBox">
|
||||
<div :style="openBox ? 'right: 0;width:120px;cursor: auto;' : ''">
|
||||
<font-awesome-icon class="icon global focusing" :icon="['fas', 'compass']" spin style="color: #ffffff;" />
|
||||
<span :style="openBox ? 'display: block;color: #fff;cursor: pointer;' : ''">{{$t('side_task.novice_task')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ms-site-task ref="siteTask" :taskData="taskData" @closeBox="closeBox"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsSiteTask from "../../components/sidemenu/components/SiteTask";
|
||||
import {getSideTask} from "../../api/novice";
|
||||
import {TASK_DATA} from "../../utils/constants";
|
||||
|
||||
|
||||
export default {
|
||||
name: "MsSideTask",
|
||||
components: { MsSiteTask },
|
||||
|
||||
data() {
|
||||
return {
|
||||
taskStatus: false,
|
||||
openBox:false,
|
||||
totalTask: 0,
|
||||
taskData:[]
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initTaskData()
|
||||
},
|
||||
methods: {
|
||||
initTaskData(){
|
||||
getSideTask().then(res=>{
|
||||
if(res.data.length > 0 && res.data[0].dataOption){
|
||||
this.taskData = JSON.parse(res.data[0].dataOption)
|
||||
}else{
|
||||
this.taskData = TASK_DATA
|
||||
}
|
||||
let microApp = JSON.parse(sessionStorage.getItem("micro_apps"));
|
||||
let num = 0
|
||||
let total = 0
|
||||
this.taskData.forEach(item =>{
|
||||
if(!(microApp && microApp[item.name])){
|
||||
item.status = -1
|
||||
total++
|
||||
} else {
|
||||
item.percentage = Math.floor(item.rate / item.taskData.length * 100)
|
||||
if(item.percentage === 100){
|
||||
item.status = 1
|
||||
}else if(100 > item.percentage && item.percentage > 0){
|
||||
item.status = 2
|
||||
}
|
||||
num += item.rate
|
||||
}
|
||||
})
|
||||
if(total < this.taskData.length){
|
||||
this.taskStatus = true
|
||||
}
|
||||
this.totalTask = num
|
||||
})
|
||||
},
|
||||
toggle(){
|
||||
this.openBox = true
|
||||
this.initTaskData()
|
||||
this.$refs.siteTask.open();
|
||||
},
|
||||
closeBox(status){
|
||||
this.openBox = status
|
||||
},
|
||||
skipOpen(path){
|
||||
this.initTaskData()
|
||||
this.$refs.siteTask.skipOpen(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.parentBox {
|
||||
height: 100%;
|
||||
background: gainsboro;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
}
|
||||
.parentBox .contentsBox div {
|
||||
transition: all 1s;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
width: 27px;
|
||||
border-radius: 50px;
|
||||
background-color: #783787;
|
||||
color: #fff;
|
||||
padding: 3px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.parentBox .contentsBox div span {
|
||||
display: none;
|
||||
color: #fff;
|
||||
}
|
||||
.parentBox .contentsBox div span:last-child {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.parentBox .contentsBox div:nth-child(1) {
|
||||
bottom: 100px;
|
||||
}
|
||||
.parentBox .contentsBox div:hover {
|
||||
right: 0;
|
||||
height: 28px;
|
||||
width: 120px;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.parentBox .contentsBox div:hover span {
|
||||
display: block;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.parentBox .contentsBox div:not(:last-child) {
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
/*隐藏浏览器滚动条*/
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 2em !important;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,320 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="csat-popup" v-if="cardVisible">
|
||||
<template v-for="(item,index) in taskInfo" >
|
||||
<el-card :key="item.id" v-if="(index + 1) === taskIndex" class="box-card" >
|
||||
<div slot="header" class="clearfix">
|
||||
<span class="text-header">{{$t(item.title)}}</span>
|
||||
<el-button style="float: right; padding: 5px 0;" class="moon" type="text" @click="open()">
|
||||
<font-awesome-icon :icon="['fa', 'chevron-down']" class="icon"/>
|
||||
</el-button>
|
||||
<el-button style="float: right; padding: 3px 0;margin-right: 25px" type="text" >
|
||||
<img v-for="num in completeNum" src="../../../assets/guide/moon-dark.png"
|
||||
class="moon" alt="MS" :key="num + 'd'">
|
||||
<img v-for="num in ongoingNum" src="../../../assets/guide/moon-light.png"
|
||||
class="moon" alt="MS" :key="num + 'l'">
|
||||
<img v-for="num in incompleteNum" src="../../../assets/guide/moon.png"
|
||||
class="moon" alt="MS" :key="num">
|
||||
</el-button>
|
||||
<el-progress :percentage="item.percentage" color="#783787" class="progress-card"></el-progress>
|
||||
</div>
|
||||
<div style="height: 220px">
|
||||
<template v-for="(val,i) in item.taskData">
|
||||
<div class="text item" v-permission="val.permission" :key="i">
|
||||
<p v-if="val.status === 1">
|
||||
<font-awesome-icon :icon="['far', 'check-circle']" style="color:#783887" />
|
||||
<label> {{$t(val.name)}}</label>
|
||||
</p>
|
||||
<p v-else @click="openGif(val)">
|
||||
<font-awesome-icon :icon="['far', 'circle']" class="title-icon" />
|
||||
<span> {{$t(val.name)}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<el-button v-if="taskIndex < totalNum" style="float: right; margin-left: 10px; padding: 15px 0" type="text" @click="next()">
|
||||
{{$t('side_task.next')}}
|
||||
</el-button>
|
||||
<el-button v-if="taskIndex > 1" style="float: right;margin-left: 10px; padding: 15px 0" type="text" @click="prev()">
|
||||
{{$t('side_task.prev')}}
|
||||
</el-button>
|
||||
<el-button style="float: right; padding: 15px 0;color:#8C8C8C" type="text" @click="skip()">
|
||||
{{$t('side_task.skip')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
<div class="csat-popup-gif" v-if="gifVisible">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="float: right; padding: 5px 0;" class="moon" @click="closeGif()">
|
||||
<font-awesome-icon :icon="['fa', 'times']" class="icon"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text" style="text-align: center;height: 210px">
|
||||
<el-image
|
||||
style="width: 340px;border-radius: 4px;"
|
||||
:src="gifData.url"
|
||||
:preview-src-list="[gifData.url]" lazy>
|
||||
<div slot="placeholder" class="image-slot">
|
||||
加载中<span class="dot">...</span>
|
||||
</div>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="gif-footer">
|
||||
<el-button type="primary" round size="small" class="is-plain" @click="gotoPath(gifData.path)">
|
||||
{{$t(gifData.name)}}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {hasLicense} from "../../../utils/permission";
|
||||
import {TASK_DATA, TASK_MODULE} from "../../../utils/constants";
|
||||
import {getSideTask} from "../../../api/novice";
|
||||
|
||||
export default {
|
||||
name: "SiteTask",
|
||||
data() {
|
||||
return {
|
||||
cardVisible: false,
|
||||
gifVisible: false,
|
||||
gifData:'',
|
||||
taskIndex: 1,
|
||||
taskInfo: [],
|
||||
interfaceData: '',
|
||||
performanceData: '',
|
||||
projectData: '',
|
||||
uiData: '',
|
||||
completeNum: 0,
|
||||
ongoingNum: 0,
|
||||
incompleteNum: 0,
|
||||
totalNum:0,
|
||||
status: this.$route.query.status
|
||||
}
|
||||
},
|
||||
props: {
|
||||
taskData: Array
|
||||
},
|
||||
|
||||
created() {
|
||||
console.log("this.status")
|
||||
console.log(this.status)
|
||||
if(this.status){
|
||||
this.skipOpen("/track/case/all")
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
taskNum() {
|
||||
let totalNum = 0
|
||||
let completeNum = 0
|
||||
let ongoingNum = 0
|
||||
this.taskInfo = []
|
||||
// status -1 服务未启动 0服务启动,任务未开始 1完成 2进行中
|
||||
this.taskData.forEach(item=>{
|
||||
if(item.status === 1){
|
||||
completeNum++;
|
||||
}else if(item.status === 2){
|
||||
ongoingNum++;
|
||||
}
|
||||
if(item.status !== -1) {
|
||||
totalNum++
|
||||
this.taskInfo.push(item)
|
||||
}
|
||||
})
|
||||
let incompleteNum = totalNum - completeNum - ongoingNum
|
||||
this.completeNum = completeNum
|
||||
this.ongoingNum = ongoingNum
|
||||
this.incompleteNum = incompleteNum
|
||||
this.totalNum = totalNum
|
||||
},
|
||||
|
||||
open() {
|
||||
this.taskIndex = 1
|
||||
this.taskNum()
|
||||
this.cardVisible = !this.cardVisible;
|
||||
this.$emit("closeBox", this.cardVisible)
|
||||
this.gifVisible = this.cardVisible ? this.gifVisible : this.cardVisible;
|
||||
},
|
||||
openGif(gif) {
|
||||
this.gifVisible = true
|
||||
this.gifData = gif
|
||||
},
|
||||
closeGif(){
|
||||
this.gifVisible = false
|
||||
},
|
||||
prev() {
|
||||
this.taskIndex = this.taskIndex - 1
|
||||
this.taskIndex = this.taskIndex < 1 ? 1 : this.taskIndex
|
||||
},
|
||||
next() {
|
||||
this.taskIndex = this.taskIndex + 1
|
||||
this.taskIndex = this.taskIndex > this.taskData.length ? this.taskData.length : this.taskIndex
|
||||
},
|
||||
skip() {
|
||||
this.open()
|
||||
},
|
||||
gotoPath(path){
|
||||
this.$router.push(path)
|
||||
},
|
||||
skipOpen(path){
|
||||
if(path){
|
||||
this.taskData.forEach(val=>{
|
||||
val.taskData.forEach(item=>{
|
||||
if(item.path === path){
|
||||
this.taskIndex = 1
|
||||
this.taskNum()
|
||||
this.cardVisible = true
|
||||
this.$emit("closeBox", this.cardVisible)
|
||||
this.openGif(item)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.text {
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 10px;
|
||||
margin-left: 35px;
|
||||
}
|
||||
|
||||
.item p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.item p label {
|
||||
color:#783887;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
.item p span {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
.title-icon {
|
||||
color: #303133
|
||||
}
|
||||
.item p:hover {
|
||||
color:#783887;
|
||||
cursor:pointer;
|
||||
}
|
||||
.icon {
|
||||
color: #0a0a0a;
|
||||
}
|
||||
.icon:hover {
|
||||
color: #783787;
|
||||
}
|
||||
.item p:hover .title-icon {
|
||||
color:#783887;
|
||||
}
|
||||
|
||||
.progress-card {
|
||||
margin-top: 10px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 95%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.gif-footer {
|
||||
width: 100%;
|
||||
margin: 10px 0 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.csat-popup {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 160px;
|
||||
width: 400px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
-webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #EBEEF5;
|
||||
background-color: #fff;
|
||||
-webkit-transition: .3s;
|
||||
transition: .3s;
|
||||
}
|
||||
|
||||
.csat-popup-gif {
|
||||
position: fixed;
|
||||
right: 426px;
|
||||
bottom: 160px;
|
||||
width: 400px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
-webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #EBEEF5;
|
||||
background-color: #fff;
|
||||
-webkit-transition: .3s;
|
||||
transition: .3s;
|
||||
}
|
||||
|
||||
.text-header {
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
}
|
||||
.moon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
::v-deep .csat-popup .el-card__header {
|
||||
border-bottom: none;
|
||||
padding: 20px 24px 10px 24px;
|
||||
}
|
||||
|
||||
::v-deep .csat-popup-gif .el-card__header {
|
||||
border-bottom: none;
|
||||
padding: 20px 20px 10px 24px;
|
||||
}
|
||||
|
||||
::v-deep .el-progress__text{
|
||||
color: #783787 !important;
|
||||
float: left;
|
||||
margin-left: 0;
|
||||
margin-right: 10px
|
||||
}
|
||||
|
||||
|
||||
</style>
|
|
@ -519,6 +519,9 @@ const message = {
|
|||
ui_module: "default",
|
||||
},
|
||||
other: "Other",
|
||||
function_introduction: 'Function introduction',
|
||||
page_guidance: 'Page guidance',
|
||||
novice_journey: 'Novice Journey'
|
||||
},
|
||||
login: {
|
||||
normal_Login: "Normal Login",
|
||||
|
@ -3529,6 +3532,102 @@ const message = {
|
|||
ui_title: "UI testing tasks",
|
||||
perf_title: "Perf testing tasks",
|
||||
},
|
||||
shepherd: {
|
||||
step1: {
|
||||
title: 'A Workspaces and Projects',
|
||||
text: 'MeterSphere uses "workspaces" and "projects" to organize test data and members. You can switch positions as you like in the top menu.'
|
||||
},
|
||||
step2: {
|
||||
title: 'Side navigation menu',
|
||||
text: 'The navigation menu shows which function module you are in.'
|
||||
},
|
||||
step3: {
|
||||
title: 'One workspace holds multiple projects',
|
||||
text: 'A "project" is a collection of use cases and members. Various types of tests on MeterSphere are viewed and managed through projects.'
|
||||
},
|
||||
step4: {
|
||||
title: 'Top function menu',
|
||||
text: 'The topl function menu supports switching subdivision functions under the current first-class module.'
|
||||
},
|
||||
step5: {
|
||||
title: "Where are you?",
|
||||
text: "Now, you have joined our preset workspace by default and become a member of the demo project. Please start your testing journey from here."
|
||||
},
|
||||
exit:'skip',
|
||||
next: 'Next',
|
||||
know:'know',
|
||||
},
|
||||
guide: {
|
||||
home: {
|
||||
title: 'Welcome to MeterSphere!',
|
||||
desc: 'A quickstart guide to see what MeterSphere can do for you.',
|
||||
button: 'Lets get started'
|
||||
},
|
||||
test: {
|
||||
title: 'Test cases are the cornerstone of testing',
|
||||
desc: '<span>Maintain your test cases through online editing/file import/URL synchronization/multi-person review,</span><br> <span>add them to your test plan,quantitatively manage test progress, record results, synchronize issues, </span><br> retain/share test reports, and cover the entire software testing life cycle. ',
|
||||
button: 'Next: Interface Test'
|
||||
},
|
||||
api: {
|
||||
title: 'Simulate real scenarios to automate API testing',
|
||||
desc: '<span>API testing is triggered by manual/scheduled tasks/plug-ins, supporting multiple communication protocols; </span><br> <span> scenario case-sets are arranged based on real business processes, </span><br> <span> and multi-type controllers/custom scripts/assertions are supported to meet various user needs. ',
|
||||
button: 'Next: UI Test'
|
||||
},
|
||||
ui: {
|
||||
title: 'Portable UI element library and instruction set',
|
||||
desc: '<span>Arrange scenario cases based on reusable element libraries and instructions;</span><br> combine your commonly used test steps into new instructions, <br> <span> which can be flexibly called in automation scenarios. </span>',
|
||||
button: 'Next: Performance Test'
|
||||
},
|
||||
performance: {
|
||||
title: 'One-click launch performance testing',
|
||||
desc: '<span>Provides a distributed performance testing solution, supporting multiple types of testing resource pools </span><br> <span>such as physical machines/virtual machines/k8s container clusters; </span><br> <span>One-click to initiate API scenario case to performance testing, and view real-time reports;</span><br> Provides report comparison under different configurations to control performance bottlenecks and optimize them. ',
|
||||
button: 'To create your first test case'
|
||||
},
|
||||
go_prev: 'Return to previous'
|
||||
},
|
||||
side_task: {
|
||||
test_tracking: {
|
||||
title: "Challenging Test Track",
|
||||
task_1: "Join a project",
|
||||
task_2: "Create a functional test case",
|
||||
task_3: "Create a review plan",
|
||||
task_4: "Post a comment in a review",
|
||||
task_5: "Create a test plan",
|
||||
task_6: "Add test cases to test plan",
|
||||
task_7: "Add ralated issue to test case",
|
||||
},
|
||||
api_test: {
|
||||
title: "Challenging API Test",
|
||||
task_1: "Create an API definition",
|
||||
task_2: "Import local API definition or API cases",
|
||||
task_3: "Execute an API testing",
|
||||
task_4: "Create a new test case based on API testing",
|
||||
task_5: "Share API documents",
|
||||
task_6: "Create an automation scenario case",
|
||||
task_7: "Execute automated API testing with scheduled task ",
|
||||
},
|
||||
performance_test: {
|
||||
title: "Challenging Performance Test",
|
||||
task_1: "Convert API scenario case into performance testing",
|
||||
task_2: "Share performance testing report",
|
||||
},
|
||||
project_setting: {
|
||||
title: "Challenging Project Settings",
|
||||
task_1: "create a new project",
|
||||
task_2: "Add a project member",
|
||||
task_3: "Create a project environment",
|
||||
},
|
||||
ui_test: {
|
||||
title: "Challenging UI Test",
|
||||
task_1: "Create an element",
|
||||
task_2: "Create an automated UI scenario case",
|
||||
task_3: "Execute an automated UI scenario case",
|
||||
},
|
||||
next: "Next",
|
||||
prev: "Previous",
|
||||
skip: "Skip",
|
||||
novice_task: "Novice Task"
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -511,6 +511,9 @@ const message = {
|
|||
template_delete: "模版删除",
|
||||
scope: "应用场景",
|
||||
other: "其他",
|
||||
function_introduction: '功能介绍',
|
||||
page_guidance: '页面指引',
|
||||
novice_journey: '新手旅程'
|
||||
},
|
||||
login: {
|
||||
normal_Login: "普通登录",
|
||||
|
@ -3400,6 +3403,102 @@ const message = {
|
|||
envrionment: {
|
||||
export_variable_tip: "导出接口测试变量",
|
||||
},
|
||||
shepherd: {
|
||||
step1: {
|
||||
title: '工作空间和项目',
|
||||
text: 'MeterSphere 使用「工作空间」和「项目」来组织测试数据和人力资源。您可以在顶部菜单任意切换位置。'
|
||||
},
|
||||
step2: {
|
||||
title: '功能主菜单',
|
||||
text: '主菜单显示您所在的功能模块。'
|
||||
},
|
||||
step3: {
|
||||
title: '一个空间可以创建多个项目',
|
||||
text: '「项目」是一组用例和成员的集合。MeterSphere 上各种类型的测试均通过项目进行分权分域查看和管理。'
|
||||
},
|
||||
step4: {
|
||||
title: '一级功能菜单',
|
||||
text: '顶部一级功能菜单栏,支持在当前功能模块下切换细分功能。'
|
||||
},
|
||||
step5: {
|
||||
title: "你在哪?",
|
||||
text: "现在,您已默认加入了我们预置的演示空间,并成为演示项目的一员。就从这里开始你的测试之旅吧。"
|
||||
},
|
||||
exit:'跳过',
|
||||
next:'下一步',
|
||||
know:'知道啦',
|
||||
},
|
||||
guide: {
|
||||
home: {
|
||||
title: '欢迎来到 MeterSphere!',
|
||||
desc: '通过一个快捷指引来了解 MeterSphere 究竟能为你做哪些事。',
|
||||
button: '让我们开始吧'
|
||||
},
|
||||
test: {
|
||||
title: '测试用例是测试任务的基石',
|
||||
desc: '<span>使用在线编辑/文件导入/URL同步/多人评审的方式维护你的用例,</span><br> <span>将它们加入你的测试计划中,量化管理测试进度,记录结果,同步缺陷,</span><br> 留存/分享测试报告,覆盖整个测试生命周期。',
|
||||
button: '下一个:接口测试'
|
||||
},
|
||||
api: {
|
||||
title: '模拟真实场景 让接口自动化',
|
||||
desc: '<span>通过手动/定时任务/插件触发接口测试,支持多种通信协议;</span><br> <span> 基于真实业务流程编排场景化用例集,支持添加多类型控制器/自定义脚本/断言,</span><br> 满足各种复杂场景所需。',
|
||||
button: '下一个:UI测试'
|
||||
},
|
||||
ui: {
|
||||
title: '可移植的 UI 元素库与指令集',
|
||||
desc: '<span>基于可复用的元素库及指令快速编排场景化用例;</span><br> 将你常用的测试步骤组合成新的自定义指令,在自动化场景中灵活调用。<span> <br><br></span>',
|
||||
button: '下一个:性能测试'
|
||||
},
|
||||
performance: {
|
||||
title: '性能测试 一键就可以',
|
||||
desc: '<span>提供分布式压测解决方案,支持物理机/虚拟机/k8s容器集群等多类型压测资源池;</span><br> <span>使用接口测试转性能一键发起,实时查看报告;</span><br> 提供差异配置下的报告对比,掌控性能瓶颈及调优。',
|
||||
button: '完成!去创建你的第 1 条测试用例'
|
||||
},
|
||||
go_prev: '返回上一个'
|
||||
},
|
||||
side_task: {
|
||||
test_tracking: {
|
||||
title: "通关测试跟踪",
|
||||
task_1: "加入一个项目",
|
||||
task_2: "创建一条功能用例",
|
||||
task_3: "发起一个评审计划",
|
||||
task_4: "在评审中发布评论",
|
||||
task_5: "发布一个测试计划",
|
||||
task_6: "为测试计划添加用例",
|
||||
task_7: "为测试用例关联缺陷",
|
||||
},
|
||||
api_test: {
|
||||
title: "通关接口测试",
|
||||
task_1: "创建一条接口定义",
|
||||
task_2: "导入本地接口或接口用例",
|
||||
task_3: "进行一次接口快捷调试",
|
||||
task_4: "基于接口调试创建新用例",
|
||||
task_5: "分享接口文档",
|
||||
task_6: "创建一条场景自动化用例",
|
||||
task_7: "以定时任务执行接口自动化测试",
|
||||
},
|
||||
performance_test: {
|
||||
title: "通关性能测试",
|
||||
task_1: "将接口场景用例转为性能测试",
|
||||
task_2: "分享性能测试报告",
|
||||
},
|
||||
project_setting: {
|
||||
title: "通关项目设置",
|
||||
task_1: "创建一个新项目",
|
||||
task_2: "添加一位项目成员",
|
||||
task_3: "创建项目环境",
|
||||
},
|
||||
ui_test: {
|
||||
title: "通关 UI 测试",
|
||||
task_1: "创建一个元素",
|
||||
task_2: "创建一个 UI 自动化场景",
|
||||
task_3: "执行一个 UI 自动化场景",
|
||||
},
|
||||
next: "下一章",
|
||||
prev: "上一章",
|
||||
skip: "跳过",
|
||||
novice_task: "新手旅程"
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -510,6 +510,9 @@ const message = {
|
|||
},
|
||||
template_delete: "模版刪除",
|
||||
other: "其他",
|
||||
function_introduction: '功能介紹',
|
||||
page_guidance: '頁面指引',
|
||||
novice_journey: '新手旅程'
|
||||
},
|
||||
login: {
|
||||
normal_Login: "普通登錄",
|
||||
|
@ -3400,6 +3403,102 @@ const message = {
|
|||
ui_title: "UI測試任務",
|
||||
perf_title: "性能測試任務",
|
||||
},
|
||||
shepherd: {
|
||||
step1: {
|
||||
title: '工作空間和項目',
|
||||
text: 'MeterSphere 使用「工作空間」和「項目」來組織測試數據和人力資源。您可以在頂部菜單任意切換位置。'
|
||||
},
|
||||
step2: {
|
||||
title: '功能主菜單',
|
||||
text: '主菜單顯示您所在的功能模塊。'
|
||||
},
|
||||
step3: {
|
||||
title: '一個空間可以創建多個項目',
|
||||
text: '「項目」是一組用例和成員的集合。 MeterSphere 上各種類型的測試均通過項目進行分權分域查看和管理。'
|
||||
},
|
||||
step4: {
|
||||
title: '一級功能菜單',
|
||||
text: '頂部一級功能菜單欄,支持在當前功能模塊下切換細分功能。'
|
||||
},
|
||||
step5: {
|
||||
title: "你在哪?",
|
||||
text: "現在,您已默認加入了我們預置的演示空間,並成為演示項目的一員。就從這裡開始你的測試之旅吧。"
|
||||
},
|
||||
exit:'跳過',
|
||||
next:'下一步',
|
||||
know:'知道啦',
|
||||
},
|
||||
guide: {
|
||||
home: {
|
||||
title: '歡迎來到 MeterSphere!',
|
||||
desc: '通過一個快捷指引來了解 MeterSphere 究竟能為你做哪些事。',
|
||||
button: '讓我們開始吧'
|
||||
},
|
||||
test: {
|
||||
title: '測試用例是測試任務的基石',
|
||||
desc: '<span>使用在線編輯/文件導入/URL同步/多人評審的方式維護你的用例,</span><br> <span>將它們加入你的測試計劃中,量化管理測試進度,記錄結果,同步缺陷,</span><br> 留存/分享測試報告,覆蓋整個測試生命週期。 ',
|
||||
button: '下一個:接口測試'
|
||||
},
|
||||
api: {
|
||||
title: '模擬真實場景 讓接口自動化',
|
||||
desc: '<span>通過手動/定時任務/插件觸發接口測試,支持多種通信協議;</span><br> <span> 基於真實業務流程編排場景化用例集,支持添加多類型控制器/自定義腳本/斷言,</span><br> 滿足各種複雜場景所需。 ',
|
||||
button: '下一個:UI測試'
|
||||
},
|
||||
ui: {
|
||||
title: '可移植的 UI 元素庫與指令集',
|
||||
desc: '<span>基於可複用的元素庫及指令快速編排場景化用例;</span><br> 將你常用的測試步驟組合成新的自定義指令,在自動化場景中靈活調用。 <span> <br><br></span>',
|
||||
button: '下一個:性能測試'
|
||||
},
|
||||
performance: {
|
||||
title: '性能測試 一鍵就可以',
|
||||
desc: '<span>提供分佈式壓測解決方案,支持物理機/虛擬機/k8s容器集群等多類型壓測資源池;</span><br> <span>使用接口測試轉性能一鍵發起,實時查看報告;</span><br> 提供差異配置下的報告對比,掌控性能瓶頸及調優。 ',
|
||||
button: '完成!去創建你的第 1 條測試用例'
|
||||
},
|
||||
go_prev: '返回上一個'
|
||||
},
|
||||
side_task: {
|
||||
test_tracking: {
|
||||
title: "通關測試跟踪",
|
||||
task_1: "加入一個項目",
|
||||
task_2: "創建一條功能用例",
|
||||
task_3: "發起一個評審計畫",
|
||||
task_4: "在評審中發佈評論",
|
||||
task_5: "發佈一個測試計畫",
|
||||
task_6: "為測試計畫添加用例",
|
||||
task_7: "為測試用例關聯缺陷",
|
||||
},
|
||||
api_test: {
|
||||
title: "通關介面測試",
|
||||
task_1: "創建一條介面定義",
|
||||
task_2: "導入本地介面或介面用例",
|
||||
task_3: "進行一次介面快捷調試",
|
||||
task_4: "基於介面調試創建新用例",
|
||||
task_5: "分享介面檔案",
|
||||
task_6: "創建一條場景自動化用例",
|
||||
task_7: "以定時任務執行介面自動化測試",
|
||||
},
|
||||
performance_test: {
|
||||
title: "通關性能測試",
|
||||
task_1: "將介面場景用例轉為性能測試",
|
||||
task_2: "分享性能測試報告",
|
||||
},
|
||||
project_setting: {
|
||||
title: "通關項目設定",
|
||||
task_1: "創建一個新項目",
|
||||
task_2: "添加一比特項目成員",
|
||||
task_3: "創建項目環境",
|
||||
},
|
||||
ui_test: {
|
||||
title: "通關 UI 測試",
|
||||
task_1: "創建一個元素",
|
||||
task_2: "創建一個 UI 自動化場景",
|
||||
task_3: "執行一個 UI 自動化場景",
|
||||
},
|
||||
next: "下一章",
|
||||
prev: "上一章",
|
||||
skip: "跳過",
|
||||
novice_task: "新手旅程"
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -15,6 +15,9 @@ import "./router/permission";
|
|||
import "./micro-app";
|
||||
import mavonEditor from 'mavon-editor';
|
||||
import 'mavon-editor/dist/css/index.css';
|
||||
import VueShepherd from 'vue-shepherd' // 新手引导
|
||||
import './assets/shepherd/shepherd-theme.css'
|
||||
import { gotoCancel, gotoNext } from "./utils";
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
@ -33,6 +36,10 @@ Vue.use(directives);
|
|||
Vue.use(filters);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
Vue.use(mavonEditor);
|
||||
Vue.use(VueShepherd)
|
||||
|
||||
Vue.prototype.gotoCancel = gotoCancel
|
||||
Vue.prototype.gotoNext = gotoNext
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import axios from 'axios'
|
||||
import {$error} from "./message"
|
||||
import {getCurrentProjectID, getCurrentWorkspaceId} from "../utils/token";
|
||||
import {PROJECT_ID, TokenKey, WORKSPACE_ID} from "../utils/constants";
|
||||
import {PROJECT_ID, TokenKey, WORKSPACE_ID, TASK_PATH, TASK_DATA} from "../utils/constants";
|
||||
import packageJSON from '@/../package.json'
|
||||
import {getUrlParams, getUUID} from "../utils";
|
||||
import {initTaskData} from "../api/novice";
|
||||
import {Base64} from "js-base64";
|
||||
|
||||
// baseURL 根据是否是独立运行修改
|
||||
|
@ -94,9 +95,17 @@ const checkPermission = response => {
|
|||
}
|
||||
}
|
||||
|
||||
const checkTask = response => {
|
||||
// 请根据实际需求修改
|
||||
if (TASK_PATH.includes(response.config["url"]) && response.status === 200) {
|
||||
initTaskData(response.config["url"]);
|
||||
}
|
||||
}
|
||||
|
||||
// 请根据实际需求修改
|
||||
instance.interceptors.response.use(response => {
|
||||
checkAuth(response);
|
||||
checkTask(response);
|
||||
return response;
|
||||
}, error => {
|
||||
let msg;
|
||||
|
|
|
@ -263,3 +263,103 @@ export const SECOND_LEVEL_ROUTE_PERMISSION_MAP = {
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const TASK_PATH = [
|
||||
"/test/case/add",
|
||||
"/test/case/review/save",
|
||||
"/test/case/review/comment/save",
|
||||
"/test/plan/add",
|
||||
"/test/plan/relevance",
|
||||
"/issues/add",
|
||||
"/api/definition/create",
|
||||
"/api/definition/run/debug",
|
||||
"/api/testcase/create",
|
||||
"/share/info/generateApiDocumentShareInfo",
|
||||
"/api/definition/import",
|
||||
"/api/automation/create",
|
||||
"/api/automation/schedule/update",
|
||||
"/performance/save",
|
||||
"/share/info/generateShareInfoWithExpired",
|
||||
"/project/add",
|
||||
"/project/member/add",
|
||||
"/user/project/member/add",
|
||||
"/api/environment/add",
|
||||
"/ui/element/add",
|
||||
"/ui/automation/create",
|
||||
"/ui/automation/run/debug",
|
||||
];
|
||||
|
||||
export const TASK_DATA = [
|
||||
{
|
||||
id: 1,
|
||||
name: "track",
|
||||
title: "side_task.test_tracking.title",
|
||||
percentage: 14,
|
||||
taskData: [
|
||||
{ id: 1, name: "side_task.test_tracking.task_1", status: 1, permission: ['PROJECT_MANAGER:READ', 'WORKSPACE_PROJECT_MANAGER:READ'], api: [''], path: '/setting/project/:type', url: "" },
|
||||
{ id: 2, name: "side_task.test_tracking.task_2", status: 0, permission: ['PROJECT_TRACK_CASE:READ+CREATE'], api: ["/test/case/add"], path: '/track/case/all', url: "/assets/guide/track/task-2.gif" },
|
||||
{ id: 3, name: "side_task.test_tracking.task_3", status: 0, permission: ['PROJECT_TRACK_REVIEW:READ+CREATE'], api: ["/test/case/review/save"], path: '/track/review/all', url: "/assets/guide/track/task-3.gif" },
|
||||
{ id: 4, name: "side_task.test_tracking.task_4", status: 0, permission: ['PROJECT_TRACK_REVIEW:READ+COMMENT'], api: ["/test/case/review/comment/save"], path: '/track/review/all', url: "/assets/guide/track/task-4.gif" },
|
||||
{ id: 5, name: "side_task.test_tracking.task_5", status: 0, permission: ['PROJECT_TRACK_PLAN:READ+CREATE'], api: ["/test/plan/add"], path: '/track/plan/all', url: "/assets/guide/track/task-5.gif" },
|
||||
{ id: 6, name: "side_task.test_tracking.task_6", status: 0, permission: ['PROJECT_TRACK_PLAN:READ+RELEVANCE_OR_CANCEL'], api: ["/test/plan/relevance"], path: '/track/plan/all', url: "/assets/guide/track/task-6.gif" },
|
||||
{ id: 7, name: "side_task.test_tracking.task_7", status: 0, permission: ['PROJECT_TRACK_ISSUE:READ+CREATE'], api: ["/issues/add"], path: '/track/issue', url: "/assets/guide/track/task-7.gif" },
|
||||
],
|
||||
rate: 1,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "api",
|
||||
title: 'side_task.api_test.title',
|
||||
percentage: 0,
|
||||
taskData: [
|
||||
{id: 1, name: "side_task.api_test.task_1", status: 0, path: '/api/definition', permission: ['PROJECT_API_DEFINITION:READ+CREATE_API'], api: ["/api/definition/create"], url: "/assets/guide/api/task-1.gif" },
|
||||
{id: 2, name: "side_task.api_test.task_2", status: 0, path: '/api/definition', permission: ['PROJECT_API_DEFINITION:READ+IMPORT_API'], api: ["/api/definition/import"], url: "/assets/guide/api/task-2.gif" },
|
||||
{id: 3, name: "side_task.api_test.task_3", status: 0, path: '/api/definition', permission: ['PROJECT_API_DEFINITION:READ+DEBUG'], api: ["/api/definition/run/debug"], url: "/assets/guide/api/task-3.gif" },
|
||||
{id: 4, name: "side_task.api_test.task_4", status: 0, path: '/api/definition', permission: ['PROJECT_API_DEFINITION:READ+CREATE_CASE'], api: ["/api/testcase/create"], url: "/assets/guide/api/task-4.gif" },
|
||||
{id: 5, name: "side_task.api_test.task_5", status: 0, path: '/api/automation', permission: ['PROJECT_API_DEFINITION:READ'], api: ["/share/info/generateApiDocumentShareInfo"], url: "/assets/guide/api/task-5.gif" },
|
||||
{id: 6, name: "side_task.api_test.task_6", status: 0, path: '/api/automation', permission: ['PROJECT_API_SCENARIO:READ+CREATE'], api: ["/api/automation/create"], url: "/assets/guide/api/task-6.gif" },
|
||||
{id: 7, name: "side_task.api_test.task_7", status: 0, path: '/api/automation', permission: ['PROJECT_API_SCENARIO:READ+SCHEDULE'], api: ["/api/automation/schedule/update"], url: "/assets/guide/api/task-7.gif" },
|
||||
],
|
||||
rate: 0,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "performance",
|
||||
title: 'side_task.performance_test.title',
|
||||
percentage: 0,
|
||||
taskData: [
|
||||
{id: 1, name: 'side_task.performance_test.task_1', status: 0, path: '/performance/test/all', permission: ['PROJECT_API_SCENARIO:READ+CREATE_PERFORMANCE',"PROJECT_API_SCENARIO:READ+CREATE_PERFORMANCE_BATCH"], api: ["/performance/save"], url: "/assets/guide/performance/task-1.gif" },
|
||||
{id: 2, name: 'side_task.performance_test.task_2', status: 0, path: '/performance/report/all', permission: ['PROJECT_PERFORMANCE_REPORT:READ'], api: ["/share/info/generateShareInfoWithExpired"], url: "/assets/guide/performance/task-2.gif" },
|
||||
],
|
||||
rate: 0,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "project",
|
||||
title: 'side_task.project_setting.title',
|
||||
percentage: 0,
|
||||
taskData: [
|
||||
{id: 1, name: 'side_task.project_setting.task_1', status: 0, permission: ['WORKSPACE_PROJECT_MANAGER:READ+CREATE'], api: ["/project/add"], path: '/setting/project/:type', url: "/assets/guide/project/task-1.gif" },
|
||||
{id: 2, name: 'side_task.project_setting.task_2', status: 0, permission: ['PROJECT_USER:READ+CREATE'], api: ["/project/member/add","/user/project/member/add"], path: '/project/member', url: "/assets/guide/project/task-2.gif" },
|
||||
{id: 3, name: 'side_task.project_setting.task_3', status: 0, permission: ['PROJECT_ENVIRONMENT:READ+CREATE'], api: ["/api/environment/add"], path: '/project/env', url: "/assets/guide/project/task-3.gif" },
|
||||
],
|
||||
rate: 0,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "ui",
|
||||
title: 'side_task.ui_test.title',
|
||||
percentage: 0,
|
||||
taskData: [
|
||||
{id: 1, name: 'side_task.ui_test.task_1', status: 0, permission: ['PROJECT_UI_ELEMENT:READ+CREATE'], api: ["/ui/element/add"], path: '/ui/element', url: "/assets/guide/ui/task-1.gif" },
|
||||
{id: 2, name: 'side_task.ui_test.task_2', status: 0, permission: ['PROJECT_UI_ELEMENT:READ+CREATE'], api: ["/ui/automation/create"], path: '/ui/automation', url: "/assets/guide/ui/task-2.gif" },
|
||||
{id: 2, name: 'side_task.ui_test.task_3', status: 0, permission: ['PROJECT_UI_SCENARIO:READ+RUN'], api: ["/ui/automation/run/debug"], path: '/ui/report', url: "/assets/guide/ui/task-3.gif" },
|
||||
],
|
||||
rate: 0,
|
||||
status: 0
|
||||
},
|
||||
]
|
||||
|
|
|
@ -16,6 +16,7 @@ import JsPDF from "jspdf";
|
|||
* 如果编辑某一行,则只调整某一行,提升效率
|
||||
*/
|
||||
import calcTextareaHeight from "element-ui/packages/input/src/calcTextareaHeight";
|
||||
import {saveStep} from "../api/novice";
|
||||
|
||||
export function setCustomizeColor(color) {
|
||||
// 自定义主题风格
|
||||
|
@ -450,3 +451,32 @@ export function downloadPDF(ele, pdfName) {
|
|||
//可动态生成
|
||||
});
|
||||
}
|
||||
|
||||
export function goSkip(_this) {
|
||||
_this.cancel()
|
||||
}
|
||||
|
||||
export function gotoCancel(_this, cancel) {
|
||||
if (cancel) {
|
||||
_this.cancel()
|
||||
} else {
|
||||
_this.complete()
|
||||
}
|
||||
saveStep().then(res => {
|
||||
localStorage.setItem('guide', '1')
|
||||
}).catch(error => {
|
||||
// 错误的信息
|
||||
this.$error({
|
||||
message: error.response.data.message
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 上一步,下一步
|
||||
export function gotoNext(_this, path, step) {
|
||||
_this.next()
|
||||
localStorage.setItem('step', step)
|
||||
if (path) {
|
||||
this.$router.push(path)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package io.metersphere.base.mapper;
|
||||
|
||||
import io.metersphere.base.domain.NoviceStatistics;
|
||||
import io.metersphere.base.domain.NoviceStatisticsExample;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface NoviceStatisticsMapper {
|
||||
long countByExample(NoviceStatisticsExample example);
|
||||
|
||||
int deleteByExample(NoviceStatisticsExample example);
|
||||
|
||||
int deleteByPrimaryKey(String id);
|
||||
|
||||
int insert(NoviceStatistics record);
|
||||
|
||||
int insertSelective(NoviceStatistics record);
|
||||
|
||||
List<NoviceStatistics> selectByExampleWithBLOBs(NoviceStatisticsExample example);
|
||||
|
||||
List<NoviceStatistics> selectByExample(NoviceStatisticsExample example);
|
||||
|
||||
NoviceStatistics selectByPrimaryKey(String id);
|
||||
|
||||
int updateByExampleSelective(@Param("record") NoviceStatistics record, @Param("example") NoviceStatisticsExample example);
|
||||
|
||||
int updateByExampleWithBLOBs(@Param("record") NoviceStatistics record, @Param("example") NoviceStatisticsExample example);
|
||||
|
||||
int updateByExample(@Param("record") NoviceStatistics record, @Param("example") NoviceStatisticsExample example);
|
||||
|
||||
int updateByPrimaryKeySelective(NoviceStatistics record);
|
||||
|
||||
int updateByPrimaryKeyWithBLOBs(NoviceStatistics record);
|
||||
|
||||
int updateByPrimaryKey(NoviceStatistics record);
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.metersphere.base.mapper.NoviceStatisticsMapper">
|
||||
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.NoviceStatistics">
|
||||
<id column="id" jdbcType="VARCHAR" property="id" />
|
||||
<result column="user_id" jdbcType="VARCHAR" property="userId" />
|
||||
<result column="guide_step" jdbcType="TINYINT" property="guideStep" />
|
||||
<result column="guide_num" jdbcType="TINYINT" property="guideNum" />
|
||||
<result column="create_time" jdbcType="BIGINT" property="createTime" />
|
||||
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.NoviceStatistics">
|
||||
<result column="data_option" jdbcType="LONGVARCHAR" property="dataOption" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
<foreach collection="oredCriteria" item="criteria" separator="or">
|
||||
<if test="criteria.valid">
|
||||
<trim prefix="(" prefixOverrides="and" suffix=")">
|
||||
<foreach collection="criteria.criteria" item="criterion">
|
||||
<choose>
|
||||
<when test="criterion.noValue">
|
||||
and ${criterion.condition}
|
||||
</when>
|
||||
<when test="criterion.singleValue">
|
||||
and ${criterion.condition} #{criterion.value}
|
||||
</when>
|
||||
<when test="criterion.betweenValue">
|
||||
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
|
||||
</when>
|
||||
<when test="criterion.listValue">
|
||||
and ${criterion.condition}
|
||||
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
|
||||
#{listItem}
|
||||
</foreach>
|
||||
</when>
|
||||
</choose>
|
||||
</foreach>
|
||||
</trim>
|
||||
</if>
|
||||
</foreach>
|
||||
</where>
|
||||
</sql>
|
||||
<sql id="Update_By_Example_Where_Clause">
|
||||
<where>
|
||||
<foreach collection="example.oredCriteria" item="criteria" separator="or">
|
||||
<if test="criteria.valid">
|
||||
<trim prefix="(" prefixOverrides="and" suffix=")">
|
||||
<foreach collection="criteria.criteria" item="criterion">
|
||||
<choose>
|
||||
<when test="criterion.noValue">
|
||||
and ${criterion.condition}
|
||||
</when>
|
||||
<when test="criterion.singleValue">
|
||||
and ${criterion.condition} #{criterion.value}
|
||||
</when>
|
||||
<when test="criterion.betweenValue">
|
||||
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
|
||||
</when>
|
||||
<when test="criterion.listValue">
|
||||
and ${criterion.condition}
|
||||
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
|
||||
#{listItem}
|
||||
</foreach>
|
||||
</when>
|
||||
</choose>
|
||||
</foreach>
|
||||
</trim>
|
||||
</if>
|
||||
</foreach>
|
||||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, user_id, guide_step, guide_num, create_time, update_time
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
data_option
|
||||
</sql>
|
||||
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.NoviceStatisticsExample" resultMap="ResultMapWithBLOBs">
|
||||
select
|
||||
<if test="distinct">
|
||||
distinct
|
||||
</if>
|
||||
<include refid="Base_Column_List" />
|
||||
,
|
||||
<include refid="Blob_Column_List" />
|
||||
from novice_statistics
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
<if test="orderByClause != null">
|
||||
order by ${orderByClause}
|
||||
</if>
|
||||
</select>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.NoviceStatisticsExample" resultMap="BaseResultMap">
|
||||
select
|
||||
<if test="distinct">
|
||||
distinct
|
||||
</if>
|
||||
<include refid="Base_Column_List" />
|
||||
from novice_statistics
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
<if test="orderByClause != null">
|
||||
order by ${orderByClause}
|
||||
</if>
|
||||
</select>
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
,
|
||||
<include refid="Blob_Column_List" />
|
||||
from novice_statistics
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</select>
|
||||
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
|
||||
delete from novice_statistics
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</delete>
|
||||
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.NoviceStatisticsExample">
|
||||
delete from novice_statistics
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.NoviceStatistics">
|
||||
insert into novice_statistics (id, user_id, guide_step, guide_num,
|
||||
create_time, update_time, data_option)
|
||||
values (#{id,jdbcType=VARCHAR}, #{userId,jdbcType=VARCHAR}, #{guideStep,jdbcType=TINYINT},
|
||||
#{guideNum,jdbcType=INTEGER},#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{dataOption,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.NoviceStatistics">
|
||||
insert into novice_statistics
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
id,
|
||||
</if>
|
||||
<if test="userId != null">
|
||||
user_id,
|
||||
</if>
|
||||
<if test="guideStep != null">
|
||||
guide_step,
|
||||
</if>
|
||||
<if test="guideNum != null">
|
||||
guide_num,
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
create_time,
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
update_time,
|
||||
</if>
|
||||
<if test="dataOption != null">
|
||||
data_option,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
#{id,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="userId != null">
|
||||
#{userId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="guideStep != null">
|
||||
#{guideStep,jdbcType=TINYINT},
|
||||
</if>
|
||||
<if test="guideNum != null">
|
||||
#{guideNum,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
#{createTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
#{updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="dataOption != null">
|
||||
#{dataOption,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.NoviceStatisticsExample" resultType="java.lang.Long">
|
||||
select count(*) from novice_statistics
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</select>
|
||||
<update id="updateByExampleSelective" parameterType="map">
|
||||
update novice_statistics
|
||||
<set>
|
||||
<if test="record.id != null">
|
||||
id = #{record.id,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.userId != null">
|
||||
user_id = #{record.userId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.guideStep != null">
|
||||
guide_step = #{record.guideStep,jdbcType=TINYINT},
|
||||
</if>
|
||||
<if test="record.guideNum != null">
|
||||
guide_num = #{record.guideNum,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="record.createTime != null">
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="record.updateTime != null">
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="record.dataOption != null">
|
||||
data_option = #{record.dataOption,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByExampleWithBLOBs" parameterType="map">
|
||||
update novice_statistics
|
||||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
user_id = #{record.userId,jdbcType=VARCHAR},
|
||||
guide_step = #{record.guideStep,jdbcType=TINYINT},
|
||||
guide_num = #{record.guideNum,jdbcType=INTEGER},
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
data_option = #{record.dataOption,jdbcType=LONGVARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByExample" parameterType="map">
|
||||
update novice_statistics
|
||||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
user_id = #{record.userId,jdbcType=VARCHAR},
|
||||
guide_step = #{record.guideStep,jdbcType=TINYINT},
|
||||
guide_num = #{record.guideNum,jdbcType=INTEGER},
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.NoviceStatistics">
|
||||
update novice_statistics
|
||||
<set>
|
||||
<if test="userId != null">
|
||||
user_id = #{userId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="guideStep != null">
|
||||
guide_step = #{guideStep,jdbcType=TINYINT},
|
||||
</if>
|
||||
<if test="record.guideNum != null">
|
||||
guide_num = #{record.guideNum,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="dataOption != null">
|
||||
data_option = #{dataOption,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.NoviceStatistics">
|
||||
update novice_statistics
|
||||
set user_id = #{userId,jdbcType=VARCHAR},
|
||||
guide_step = #{guideStep,jdbcType=TINYINT},
|
||||
guide_num = #{record.guideNum,jdbcType=INTEGER},
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
data_option = #{dataOption,jdbcType=LONGVARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.NoviceStatistics">
|
||||
update novice_statistics
|
||||
set user_id = #{userId,jdbcType=VARCHAR},
|
||||
guide_step = #{guideStep,jdbcType=TINYINT},
|
||||
guide_num = #{record.guideNum,jdbcType=INTEGER},
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
update_time = #{updateTime,jdbcType=BIGINT}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -0,0 +1,45 @@
|
|||
package io.metersphere.novice.controller;
|
||||
|
||||
import io.metersphere.base.domain.NoviceStatistics;
|
||||
import io.metersphere.commons.constants.OperLogConstants;
|
||||
import io.metersphere.commons.constants.OperLogModule;
|
||||
import io.metersphere.log.annotation.MsAuditLog;
|
||||
import io.metersphere.notice.domain.MessageDetail;
|
||||
import io.metersphere.notice.service.NoticeService;
|
||||
import io.metersphere.novice.request.StepRequest;
|
||||
import io.metersphere.novice.service.NoviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/3/17 14:07
|
||||
* @version: 1.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "novice")
|
||||
public class NoviceController {
|
||||
@Resource
|
||||
private NoviceService noviceService;
|
||||
|
||||
@PostMapping("/info")
|
||||
public List<NoviceStatistics> getNoviceInfo() {
|
||||
return noviceService.getNoviceInfo();
|
||||
}
|
||||
|
||||
@PostMapping("/save/step")
|
||||
public void saveStep(@RequestBody StepRequest stepRequest) {
|
||||
noviceService.saveStep(stepRequest);
|
||||
}
|
||||
|
||||
@PostMapping("/save/task")
|
||||
public void saveTask(@RequestBody NoviceStatistics noviceStatistics) {
|
||||
noviceService.saveNoviceInfo(noviceStatistics);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.novice.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/3/18 17:34
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class StepRequest {
|
||||
/**
|
||||
* 新手引导截止步骤
|
||||
*/
|
||||
private Integer guideStep;
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package io.metersphere.novice.service;
|
||||
|
||||
import io.metersphere.base.domain.NoviceStatistics;
|
||||
import io.metersphere.base.domain.NoviceStatisticsExample;
|
||||
import io.metersphere.base.mapper.NoviceStatisticsMapper;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.novice.request.StepRequest;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
||||
* @date: 2023/3/17 14:18
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class NoviceService {
|
||||
@Resource
|
||||
private NoviceStatisticsMapper noviceStatisticsMapper;
|
||||
|
||||
public List<NoviceStatistics> getNoviceInfo() {
|
||||
NoviceStatisticsExample example = new NoviceStatisticsExample();
|
||||
example.createCriteria().andUserIdEqualTo(SessionUtils.getUserId());
|
||||
return noviceStatisticsMapper.selectByExampleWithBLOBs(example);
|
||||
}
|
||||
|
||||
public void saveNoviceInfo(NoviceStatistics noviceStatistics) {
|
||||
List<NoviceStatistics> noviceInfo = getNoviceInfo();
|
||||
long systemTime = System.currentTimeMillis();
|
||||
if(noviceInfo != null && noviceInfo.size() > 0){
|
||||
NoviceStatistics record = noviceInfo.get(0);
|
||||
NoviceStatisticsExample example = new NoviceStatisticsExample();
|
||||
example.createCriteria().andUserIdEqualTo(SessionUtils.getUserId()).andIdEqualTo(record.getId());
|
||||
noviceStatistics.setUpdateTime(systemTime);
|
||||
noviceStatisticsMapper.updateByExampleSelective(noviceStatistics, example);
|
||||
}else{
|
||||
noviceStatistics.setId(UUID.randomUUID().toString());
|
||||
noviceStatistics.setUserId(SessionUtils.getUserId());
|
||||
noviceStatistics.setCreateTime(systemTime);
|
||||
noviceStatistics.setUpdateTime(systemTime);
|
||||
noviceStatisticsMapper.insertSelective(noviceStatistics);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveStep(StepRequest stepRequest){
|
||||
List<NoviceStatistics> noviceInfo = getNoviceInfo();
|
||||
long systemTime = System.currentTimeMillis();
|
||||
if(noviceInfo != null && noviceInfo.size() > 0){
|
||||
NoviceStatistics noviceStatistics = noviceInfo.get(0);
|
||||
noviceStatistics.setGuideStep(stepRequest.getGuideStep());
|
||||
noviceStatistics.setGuideNum(noviceStatistics.getGuideNum() + 1);
|
||||
noviceStatistics.setUpdateTime(systemTime);
|
||||
NoviceStatisticsExample example = new NoviceStatisticsExample();
|
||||
example.createCriteria().andUserIdEqualTo(SessionUtils.getUserId()).andIdEqualTo(noviceStatistics.getId());
|
||||
|
||||
noviceStatisticsMapper.updateByExampleSelective(noviceStatistics, example);
|
||||
} else{
|
||||
NoviceStatistics noviceStatistics = new NoviceStatistics();
|
||||
noviceStatistics.setId(UUID.randomUUID().toString());
|
||||
noviceStatistics.setUserId(SessionUtils.getUserId());
|
||||
noviceStatistics.setGuideStep(stepRequest.getGuideStep());
|
||||
noviceStatistics.setGuideStep(1);
|
||||
noviceStatistics.setCreateTime(systemTime);
|
||||
noviceStatistics.setUpdateTime(systemTime);
|
||||
|
||||
noviceStatisticsMapper.insertSelective(noviceStatistics);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +66,9 @@
|
|||
"vue2-ace-editor": "0.0.15",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"xml-js": "^1.6.11",
|
||||
"yan-progress": "^1.0.3"
|
||||
"yan-progress": "^1.0.3",
|
||||
"vue-shepherd": "^0.3.0",
|
||||
"shepherd.js": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<el-row type="flex">
|
||||
<project-switch :project-name="currentProject"/>
|
||||
<el-col :span="14">
|
||||
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router
|
||||
<el-menu class="header-menu shepherd-project-menu" :unique-opened="true" mode="horizontal" router
|
||||
:default-active="pathName">
|
||||
<el-menu-item :index="'/project/home'">
|
||||
{{ $t('project.info') }}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<el-row type="flex" justify="space-around" :gutter="10">
|
||||
<el-col :xs="12" :sm="12" :md="11" :lg="11" :xl="10" class="card-col">
|
||||
<el-card class="project-info-card">
|
||||
<div class="project-info-card-div">
|
||||
<div class="project-info-card-div shepherd-project-name">
|
||||
<span class="project-name">{{ project.name }}</span>
|
||||
<i class="el-icon-edit project-edit" @click="edit" v-permission="['PROJECT_MANAGER:READ+EDIT']"></i>
|
||||
<el-row class="project-item">
|
||||
|
|
|
@ -16,6 +16,9 @@ import "metersphere-frontend/src/router/permission";
|
|||
import mavonEditor from "mavon-editor";
|
||||
import "mavon-editor/dist/css/index.css";
|
||||
import VuePapaParse from "vue-papa-parse";
|
||||
import VueShepherd from 'vue-shepherd' // 新手引导
|
||||
import 'metersphere-frontend/src/assets/shepherd/shepherd-theme.css'
|
||||
import { gotoCancel, gotoNext } from "metersphere-frontend/src/utils";
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
const pinia = createPinia();
|
||||
|
@ -33,6 +36,10 @@ Vue.use(directives);
|
|||
Vue.use(filters);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
Vue.use(VuePapaParse);
|
||||
Vue.use(VueShepherd);
|
||||
|
||||
Vue.prototype.gotoCancel = gotoCancel
|
||||
Vue.prototype.gotoNext = gotoNext
|
||||
|
||||
let instance = null;
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE `novice_statistics` (
|
||||
`id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户id',
|
||||
`guide_step` tinyint NOT NULL DEFAULT '0' COMMENT '新手引导完成的步骤',
|
||||
`guide_num` int NOT NULL DEFAULT '1' COMMENT '新手引导的次数',
|
||||
`data_option` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'data option (JSON format)',
|
||||
`create_time` bigint DEFAULT NULL,
|
||||
`update_time` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
|
@ -67,7 +67,8 @@
|
|||
"vuedraggable": "^2.24.3",
|
||||
"xml-js": "^1.6.11",
|
||||
"yan-progress": "^1.0.3",
|
||||
"js-file-download": "^0.4.12"
|
||||
"vue-shepherd": "^0.3.0",
|
||||
"shepherd.js": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
|
|
|
@ -13,6 +13,9 @@ import plugins from "metersphere-frontend/src/plugins";
|
|||
import directives from "metersphere-frontend/src/directive";
|
||||
import filters from "metersphere-frontend/src/filters";
|
||||
import "metersphere-frontend/src/router/permission";
|
||||
import VueShepherd from 'vue-shepherd' // 新手引导
|
||||
import 'metersphere-frontend/src/assets/shepherd/shepherd-theme.css'
|
||||
import { gotoCancel, gotoNext } from "metersphere-frontend/src/utils";
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
@ -29,6 +32,10 @@ Vue.use(plugins);
|
|||
Vue.use(directives);
|
||||
Vue.use(filters);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
Vue.use(VueShepherd);
|
||||
|
||||
Vue.prototype.gotoCancel = gotoCancel
|
||||
Vue.prototype.gotoNext = gotoNext
|
||||
|
||||
let instance = null;
|
||||
|
||||
|
|